Linq mapping to multiple tables - asp.net-mvc

I working my way through the Apress "Pro ASP.NET MVC Framework" ( http://www.apress.com/book/view/9781430210078 ) book, and in an example the author creates a link to a db table (as well as a fake repository) using linq like this:-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq.Mapping;
namespace DomainModel.Entities
{
[Table(Name = "Products")]
public class Product
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int ProductID { get; set; }
[Column] public string Name { get; set; }
[Column] public string Description { get; set; }
[Column] public decimal Price { get; set; }
[Column] public string Category { get; set; }
public string this[string propName]
{
get {
if ((propName == "Name") && string.IsNullOrEmpty(Name))
return "Please enter a product name";
if ((propName == "Description") && string.IsNullOrEmpty(Description))
return "Please enter a description";
if ((propName == "Price") && (Price < 0))
return "Price must not be negative";
if ((propName == "Category") && string.IsNullOrEmpty(Category))
return "Please specify a category";
return null;
}
}
public string Error { get { return null; } } // Not required }
}
creating an interface:-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DomainModel.Entities;
namespace DomainModel.Abstract
{
public interface IProductsRepository
{
IQueryable Products { get; }
void SaveProduct(Product product);
void DeleteProduct(Product product);
}
}
a fake repository (not included) and then a real DB connection repository:-
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DomainModel.Abstract;
using System.Data.Linq;
using DomainModel.Entities;
namespace DomainModel.Concrete
{
public class SqlProductsRepository : IProductsRepository
{
private Table productsTable;
public SqlProductsRepository(string connectionString)
{
productsTable = (new DataContext(connectionString)).GetTable();
}
public IQueryable Products
{
get { return productsTable; }
}
public void SaveProduct(Product product)
{
EnsureValid(product, "Name", "Description", "Category", "Price");
// If it's a new product, just attach it to the DataContext
if (product.ProductID == 0)
productsTable.InsertOnSubmit(product);
else {
// If we're updating an existing product, tell the DataContext
// to be responsible for saving this instance
productsTable.Attach(product);
// Also tell the DataContext to detect any changes since the last save
productsTable.Context.Refresh(RefreshMode.KeepCurrentValues, product);
}
productsTable.Context.SubmitChanges();
}
public void DeleteProduct(Product product)
{
productsTable.DeleteOnSubmit(product);
productsTable.Context.SubmitChanges();
}
private void EnsureValid(IDataErrorInfo validatable, params string[] properties)
{
if (properties.Any(x => validatable[x] != null))
throw new InvalidOperationException("The object is invalid.");
}
}
off to a DB table "Products" with the column name specified and it works nicely. I am using this technique in a real-world app, as it handles the DB access layer nicely, but I need to be able to access several tables from my object. How can I do this - do I need to split my object up into a hierarchy that reflects its tables or can I access more than 1 table from the main object, with other additional objects hanging off it that have their own tables? If so then how do I create the ORM links between the objects and tables?
Cheers
MH

Just add the related tables to your repository interface, just as you have for Product and then create the concrete implementations in your repository class, again just as you have for Product.
I've used the same pattern on my app, I have two repositories, each handling 5-10 tables. There are two distinct groups of tables which are related, hence two repositories.
I would change the SQLRepository constructor thus:
public SqlProductsRepository(string connectionString)
{
DataContext dc = new DataContext(connectionString);
productsTable = dc.GetTable<Product>();
}
You can then extend it easily thus e.g.:
private Table<Order> ordersTable;
public SqlProductsRepository(string connectionString)
{
DataContext dc = new DataContext(connectionString);
productsTable = dc.GetTable<Product>();
ordersTable = dc.GetTable<Order>();
}
IQueryable<Order> Orders
{
get { return from o in ordersTable select o; }
}
EDIT - Answering comment
Here's an example of how to deliver subordinate objects (related tables) via this method:
[Table(Name="Projects")]
public class Project
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public Guid ID { get; set; }
[Column]
public String Name { get; set; }
[Column]
public bool Active { get; set; }
[Association(ThisKey="ID", OtherKey = "ProjectID")]
private EntitySet<ProjectDate> _projectDates = new EntitySet<ProjectDate>();
public IQueryable<ProjectDate> ProjectDates
{
get { return _projectDates.AsQueryable(); }
}
}
And the ProjectDate class for completeness
[Table(Name="ProjectDates")]
public class ProjectDate
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public Guid ID { get; set; }
[Column]
public Guid ProjectID { get; set; }
[Column]
public DateTime TargetDate { get; set; }
[Column(CanBeNull = true)]
public DateTime? ActualDate { get; set; }
[Column(CanBeNull=true, IsDbGenerated = true)]
public DateTime? Created { get; set; }
private EntityRef<Project> _project;
[Association(ThisKey = "ProjectID", Storage = "_project", OtherKey = "ID")]
public Project Project
{
get { return _project.Entity; }
set { _project.Entity = value; ProjectID = _project.Entity.ID; }
}
}

Related

Update existing entity framework with database (by adding new tables from db)

Hi i have an entity and i am gonna add two tables from database named as country and state.
There is a relation between these two tables based on CountryId.
I used the "Update Model from database ..." to add these two entity types.
I have manually written two classes for these two entity-types given as below:-
public partial class Country
{
//[Key] //[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int CountryID { get; set; }
public string CountryName { get; set; }
}
public partial class State
{
public int StateID { get; set; }
public string StateName { get; set; }
public int CountryID { get; set; }
}
public DbSet<Country> Countries { get; set; }
public DbSet<State> States { get; set; }
Controller to fetch coutries and states :-
public JsonResult GetCountries()
{
List<Country> allCountry = new List<Country>();
using (SunilEntities dc = new SunilEntities())
{
allCountry = dc.Countries.OrderBy(a => a.CountryName).ToList();
}
return new JsonResult { Data = allCountry, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
public JsonResult GetStates(int countryID)
{
List<State> allState = new List<State>();
using (SunilEntities dc = new SunilEntities())
{
allState = dc.States.Where(a => a.CountryID.Equals(countryID)).OrderBy(a => a.StateName).ToList();
}
return new JsonResult { Data = allState, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
But I am getting an error "The entity type Country is not part of the model for the current context".
What should be the exact classes to be used to use these two tables in my controller?
Is there any way I can get automated classes after entity being updated with newer tables?
Under your yourmodel.edmx file there is yourmodel.tt and it generates relevant classes, thus there is no need to write these classes. By using relevant namespace you can use them.
I got the solution change models as below:-
public partial class Country
{
public Country()
{
this.States = new HashSet<State>();
}
public int CountryID { get; set; }
public string CountryName { get; set; }
public virtual ICollection<State> States { get; set; }
}
public partial class State
{
public int StateID { get; set; }
public string StateName { get; set; }
public Nullable<int> CountryID { get; set; }
public virtual Country Country { get; set; }
public virtual State State1 { get; set; }
public virtual State State2 { get; set; }
}
and change controller as given below:-
public JsonResult GetCountries()
{
using (SunilEntities dc = new SunilEntities())
{
var ret = dc.Countries.Select(x => new { x.CountryID, x.CountryName }).ToList();
return Json(ret, JsonRequestBehavior.AllowGet);
}
}
// Fetch State by Country ID
public JsonResult GetStates(int countryID)
{
using (SunilEntities dc = new SunilEntities())
{
var ret = dc.States.Where(x => x.CountryID == countryID).Select(x => new { x.StateID, x.StateName }).ToList();
return Json(ret, JsonRequestBehavior.AllowGet);
}
}

How to get newly generated id on the MVC model

I have the model that has multiple entities. For example I have an order that have CustomerId to foreign key. In the model I have some fields from the order and some fields from the customer entity. When I am saving new record I would like to save customer first, get newly generated identity value and put it to the order table. How to achieve that? I can do that by getting MAX(ID) from the customer table, however I am pretty sure that there is better way to handle that.
This is my controller method:
public ActionResult Create(OrderModels model, FormCollection form)
{
try
{
Customer customer = new Customer() { FirstName = model.FirstName, MiddleName = model.MiddleName, SecondName = model.SecondName, Email = model.Email, PhoneNbr = model.PhoneNbr };
int orderSource = Int32.Parse(form["OrderSourceList"]);
int paymentType = Int32.Parse(form["PaymentTypeList"]);
string warehouseGuid = form["Warehouses"];
ProductLogic productLogic = new ProductLogic();
Product product = productLogic.GetProductByArticle(model.Article);
DateTime now = DateTime.Now;
SalesOrder salesOrder = new SalesOrder()
{
OrderNbr = model.OrderNbr,
OrderSourceId = orderSource,
NpWarehouseRef = new Guid(warehouseGuid),
TTN = model.TTN,
OrderStatusId = 1,
PaymentTypeId = paymentType,
OrderDate = DateTime.Now
};
using (AccountingRepository repository = new AccountingRepository())
{
repository.AddOrUpdate<Customer>(customer);
repository.AddOrUpdate<SalesOrder>(salesOrder);
}
return RedirectToAction("Index");
}
catch (Exception e)
{
return View();
}
}
AccountingRepository has dispose method
public void Dispose()
{
if (_context != null)
{
_context.SaveChanges();
_context.Dispose();
}
}
My Order class:
using System;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
namespace Accounting.Entity
{
[Table("SalesOrder", Schema = "dbo")]
public class SalesOrder
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int? Id { get; set; }
...
public int? CustomerId { get; set; }
...
public virtual Customer Customer { get; set; }
internal class Config : EntityTypeConfiguration<SalesOrder>
{
public Config()
{
HasRequired(r => r.Customer)
.WithMany(r => r.SalesOrder)
.HasForeignKey(r => r.CustomerId);
}
}
}
}
My Customer class:
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
namespace Accounting.Entity
{
[Table("Customer", Schema = "dbo")]
public class Customer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int? Id { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string SecondName { get; set; }
public string PhoneNbr { get; set; }
public string Email { get; set; }
public virtual HashSet<SalesOrder> SalesOrder { get; set; }
internal class Config : EntityTypeConfiguration<Customer>
{
public Config()
{
}
}
}
}
Two ways:
Save the customer entity, and Entity Framework will back fill the PK property with it's ID. You can then use this for the order entity.
db.Customers.Add(customer);
db.SaveChanges();
order.CustomerId = customer.Id; // has a value now
Just associate the customer with the order via a navigation property. When you save everything Entity Framework works out which relationships need to be saved first and then fills in the appropriate FK ids in the related entities.
order.Customer = customer;
db.Orders.Add(order);
db.SaveChanges(); // customer is saved first and the id is set for order.CustomerId automatically

Custom Membership with Microsoft.AspNet.Identity - CreateLocalUser fails

I've been trying to implement a custom version of the new Identity features in ASP.NET 4.5 (Microsoft.AspNet.Identity), using Visual Studio 2013. After many hours of playing around with this, I've simplified my code in an effort to get it running without errors. I've listed my code below. When doing a Local Registration, the database tables are created, but the CreateLocalUser method fails. I'm hoping that someone can help me identify the changes needed.
Models/MembershipModel.cs
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace thePulse.web.Models
{
public class PulseUser : IUser
{
public PulseUser() { }
public PulseUser(string userName)
{
UserName = userName;
}
[Key]
public string Id { get; set; }
[Required]
[StringLength(20)]
public string UserName { get; set; }
[StringLength(100)]
public string Email { get; set; }
[Column(TypeName = "Date")]
public DateTime? BirthDate { get; set; }
[StringLength(1)]
public string Gender { get; set; }
}
public class PulseUserClaim : IUserClaim
{
public PulseUserClaim() { }
[Key]
public string Key { get; set; }
public string UserId { get; set; }
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
}
public class PulseUserSecret : IUserSecret
{
public PulseUserSecret() { }
public PulseUserSecret(string userName, string secret)
{
UserName = userName;
Secret = secret;
}
[Key]
public string UserName { get; set; }
public string Secret { get; set; }
}
public class PulseUserLogin : IUserLogin
{
public PulseUserLogin() { }
public PulseUserLogin(string userId, string loginProvider, string providerKey)
{
LoginProvider = LoginProvider;
ProviderKey = providerKey;
UserId = userId;
}
[Key, Column(Order = 0)]
public string LoginProvider { get; set; }
[Key, Column(Order = 1)]
public string ProviderKey { get; set; }
public string UserId { get; set; }
}
public class PulseRole : IRole
{
public PulseRole() { }
public PulseRole(string roleId)
{
Id = roleId;
}
[Key]
public string Id { get; set; }
}
public class PulseUserRole : IUserRole
{
public PulseUserRole() { }
[Key, Column(Order = 0)]
public string RoleId { get; set; }
[Key, Column(Order = 1)]
public string UserId { get; set; }
}
public class PulseUserContext : IdentityStoreContext
{
public PulseUserContext(DbContext db) : base(db)
{
Users = new UserStore<PulseUser>(db);
Logins = new UserLoginStore<PulseUserLogin>(db);
Roles = new RoleStore<PulseRole, PulseUserRole>(db);
Secrets = new UserSecretStore<PulseUserSecret>(db);
UserClaims = new UserClaimStore<PulseUserClaim>(db);
}
}
public class PulseDbContext : IdentityDbContext<PulseUser, PulseUserClaim, PulseUserSecret, PulseUserLogin, PulseRole, PulseUserRole>
{
}
}
Changes to Controllers/AccountController.cs
public AccountController()
{
IdentityStore = new IdentityStoreManager(new PulseUserContext(new PulseDbContext()));
AuthenticationManager = new IdentityAuthenticationManager(IdentityStore);
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
try
{
// Create a profile, password, and link the local login before signing in the user
PulseUser user = new PulseUser(model.UserName);
if (await IdentityStore.CreateLocalUser(user, model.Password))
{
await AuthenticationManager.SignIn(HttpContext, user.Id, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Failed to register user name: " + model.UserName);
}
}
catch (IdentityException e)
{
ModelState.AddModelError("", e.Message);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
As I said above, this implementation fails when the CreateLocalUser method fails (Microsoft.AspNet.Identity.EntityFramework). I cannot figure out why.
The issue here is that IdentityStoreManager has strong dependency on the default implementation of identity EF models. For example, the CreateLocalUser method will create UserSecret and UserLogin objects and save them to stores, which won't work if the store is not using the default model type. So if you customize the model type, it won't work smoothly with IdentityStoreManager.
Since you only customize the IUser model, I simplified the code to inherit custom user from default identity user and reuse other models from identity EF models.
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace WebApplication11.Models
{
public class PulseUser : User
{
public PulseUser() { }
public PulseUser(string userName) : base(userName)
{
}
[StringLength(100)]
public string Email { get; set; }
[Column(TypeName = "Date")]
public DateTime? BirthDate { get; set; }
[StringLength(1)]
public string Gender { get; set; }
}
public class PulseUserContext : IdentityStoreContext
{
public PulseUserContext(DbContext db) : base(db)
{
this.Users = new UserStore<PulseUser>(this.DbContext);
}
}
public class PulseDbContext : IdentityDbContext<PulseUser, UserClaim, UserSecret, UserLogin, Role, UserRole>
{
}
}
The code above should work with preview version of Identity API.
The IdentityStoreManager API in upcoming release is already aware of this issue and changed all the non-EF dependency code into a base class so that you can customize it by inheriting from it. It should solve all the problems here. Thanks.
PulseUser.Id is defined as a string but doesn't appear to be set to a value. Were you meant to be using a GUID for the Id? If so, initialise it in the constructor.
public PulseUser() : this(String.Empty) { }
public PulseUser(string userName)
{
UserName = userName;
Id = Guid.NewGuid().ToString();
}
You will also want to perform a check that the user name doesn't already exist. Look at overriding DbEntityValidationResult in PulseDbContext. Do a new MVC project in VS2013 to see an example.
Since there are alot of changes on this when going to RTM, i have updated the SPA template that uses a WebApi controller for all the identity signin and such. Its a really cool template , if you havent seen it.
I put all my code here:
https://github.com/s093294/aspnet-identity-rtm/tree/master
(Do note, its only for inspiration. I only made it work and nothing more. Properly have a bug or two also).

Creating related attributes when creating new instance of class MVC 4 - Entity Framework

I am just getting into MVC 4 and Entity Framework 5 and want to know if what I am doing is correct?
I have a UserObject and a JobObject, the jobObject has a reference to a User Object.
public class Job
{
public int id { get; set; }
public virtual MyUser User { get; set; }
public JobType JobType { get; set; }
}
When I want to create an instance of the Job I am passing in the query string a parameter UserID, but the Job only deals with an instance of MyUser.
Is the following the correct way to associate the user to the job?
[HttpPost]
public ActionResult Create(Job job, int userid)
{
if (ModelState.IsValid)
{
MyUser staffmember = db.MyUsers.Find(userid);
if (staffmember == null)
{
return View("StaffMemberNotFound");
}
job.User = staffmember;
db.Jobs.Add(job);
db.SaveChanges();
}
}
Or is there a better way to associate the user to the job?
Your way will work but I prefer to simply work with ids if possible.
What I would suggest is that you add a MyUserId property to your Job class (remember to update the database if you are using codefirst):
public class Job
{
public int id { get; set; }
[ForeignKey("User")]
public int MyUserId { get; set: }
public virtual MyUser User { get; set; }
public JobType JobType { get; set; }
}
Then simply populate the MyUserId. You can also change your check to simply check if the id exists as apposed to finding an object and letting EF map that to a class before returning it to you
[HttpPost]
public ActionResult Create(Job job, int userid)
{
if (ModelState.IsValid)
{
if (!db.MyUsers.Any(u => u.Id == userid)
{
return View("StaffMemberNotFound");
}
job.MyUserId = userid;
db.Jobs.Add(job);
db.SaveChanges();
}
}
EF will do the rest of the mapping for you when you next retrieve the record from the database.
Your approach works fine, the only small optmization you could make is not taking the "retrieval hit" of MyUser staffmember = db.MyUsers.Find(userid); since you already have the userid.
I am using ASP.NET MVC 4 and Entity Framework 5.0, and here is my code (different model objects, but same intent as what you are doing).
Note: I let EF generate my model classes by right-clicking on the Models folder and choosing Add->ADO.NET Entity Data Model in VS.NET 2012.
Store.Models.Product
namespace Store.Models
{
using System;
using System.Collections.Generic;
public partial class Product
{
public long Id { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public System.DateTime DateAdded { get; set; }
public Nullable<long> CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
Store.Models.Category
namespace Store.Models
{
using System;
using System.Collections.Generic;
public partial class Category
{
public Category()
{
this.Products = new HashSet<Product>();
}
public long Id { get; set; }
public string CategoryName { get; set; }
public System.DateTime DateAdded { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
On my Create.cshtml page, I have the User select the CategoryId from the drop-down list. This Category Id is bound to Product.CategoryId. All I do in my method is this:
ProductController
public class ProductController : Controller
{
...
[HttpPost]
public ActionResult Create(Product product)
{
product.DateAdded = DateTime.Now;
if (dbContext != null)
{
dbContext.Products.Add(product);
dbContext.SaveChanges();
}
return RedirectToAction("Index");
}
...
}

Partial Updates for Entities with Repository/DTO patterns in MVC (prepping for API)

I've built my Domain model layer, my repository layer, and now I'm working on my DTO layer to be used by a webApi project. I'm in the middle of implementing an Update service method, and I'm wondering about partial updates. Here's my DTO class:
public class FullPersonDto
{
public FullPersonDto()
{
Friends = new List<Person>();
}
public FullPersonDto(Person person)
{
PersonId = person.PersonId;
DateCreated = person.DateCreated;
Details = person.Details;
Friends = new List<Person>();
foreach (Person friend in person.Friends)
{
Friends.Add(new PersonDto(friend));
}
}
[Key]
public int PersonId { get; set; }
[Required]
public virtual DateTime DateCreated { get; set; }
public virtual string Details { get; set; }
public List<Person> Friends { get; set; }
public Person ToEntity()
{
var person = new Person
{
PersonId = PersonId,
DateCreated = (DateTime) DateCreated,
Details = Details,
Friends = new List<Person>()
};
foreach (PersonDto friend in Friends)
{
person.Friends.Add(friend.ToEntity());
}
return person;
}
}
Here's my Update method in my Repository:
public Person UpdatePerson(Person person)
{
var entry = _db.Entry(person);
if (entry.State == EntityState.Detached)
{
var dbSet = _db.Set<Person>();
Person attachedPerson = dbSet.Find(person.PersonId);
if (attachedPerson != null)
{
var attachedEntry = _db.Entry(attachedPerson);
attachedEntry.CurrentValues.SetValues(person); // what if values are null, like ID, or DateCreated?
}
else
{
entry.State = EntityState.Modified;
}
}
SaveChanges();
return person;
}
My question is: What if I only need to update the Details of a person via my webAPI? Is the convention to construct an entire PersonDto and Update the entire object using SetValues, or is there any way I can specify that I only want a single field updated so that I don't have to send a ton of data over the wire (that I don't really need)?
If it is possible to do partial updates, when is it ever good to update the entire entity? Even if I have to update 5/7 properties, it requires that I send old data for 2/7 to re-write so that SetValues doesn't write nulls into my fields from my DTO.
Any help here would be awesome... totally new to this stuff and trying to learn everything right. Thank you.
I've taken similar approach to do optimization, and I've faced same issues with null values when attaching (not just null, you'll have issue with boolean as well). This is what I've come up with:
public static void Update<T>(this DbContext context, IDTO dto)
where T : class, IEntity
{
T TEntity = context.Set<T>().Local.FirstOrDefault(x => x.Id == dto.Id);
if (TEntity == null)
{
TEntity = context.Set<T>().Create();
TEntity.Id = dto.Id;
context.Set<T>().Attach(TEntity);
}
context.Entry(TEntity).CurrentValues.SetValues(dto);
var attribute = dto.GetAttribute<EnsureUpdatedAttribute>();
if (attribute != null)
{
foreach (var property in attribute.Properties)
context.Entry(TEntity).Property(property).IsModified = true;
}
}
That is extension method for DbContext. Here are the interfaces IDTO and IEntity:
public interface IDTO
{
int Id { get; set; }
}
public interface IEntity
{
int Id { get; set; }
Nullable<DateTime> Modified { get; set; }
Nullable<DateTime> Created { get; set; }
}
I'm using my custom EnsureUpdatedAttribute to annotate what properties should always be updated (to deal with nulls / default values not being tracked):
public class EnsureUpdatedAttribute : Attribute
{
public IEnumerable<string> Properties { get; private set; }
public EnsureUpdatedAttribute(params string[] properties)
{
Properties = properties.AsEnumerable();
}
}
And this is a sample of usage:
public class Sample : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
public Nullable<DateTime> Modified { get; set; }
public Nullable<DateTime> Created { get; set; }
}
[EnsureUpdated("Active")] /// requirement for entity framework change tracking, read about stub entities
public class SampleDTO : IDTO
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[JsonIgnore] /// How to exclude property from going on the wire / ignored for serialization
public bool Active { get; set; }
}
[HttpPost]
public HttpResponseMessage SaveSample(SampleDTO dto)
{
dto.Active = true;
_ctx.AddModel<Sample>(dto);
_ctx.SaveChanges();
return NoContent();
}
return NoContent() is just extension for returning 204 (NoContent).
Hope this helps.
Theres a few options you have, you can create a stored procedure to update the required parts (I wouldnt do this), or you can manually select the fileds to update on the model before saving the context changes with EF.
Heres an example how to update a specific field:
public void UpdatePerson(int personId, string details)
{
var person = new Person() { Id = personId, Details = details };
db.Persons.Attach(personId);
db.Entry(person).Property(x => x.Details).IsModified = true;
db.SaveChanges();
}
It will depend on your scenario what you want to do, but generally speaking its fine to send your whole entity to be updated, and this is how i would approach your situation potentially changing in the future if needed.

Resources