How to update not every fields of an object using Entity Framework and EntityState.Modified - entity-framework

I need to update all fields except property1 and property2 for the given entity object.
Having this code:
[HttpPost]
public ActionResult Add(object obj)
{
if (ModelState.IsValid)
{
context.Entry(obj).State = System.Data.EntityState.Modified;
context.SaveChanges();
}
return View(obj);
}
How to change it to add an exception to obj.property1 and obj.property2 for not being updated with this code?

Let's assume that you have a collection of the properties to be excluded:
var excluded = new[] { "property1", "property2" };
With EF5 on .NET 4.5 you can do this:
var entry = context.Entry(obj);
entry.State = EntityState.Modified;
foreach (var name in excluded)
{
entry.Property(name).IsModified = false;
}
This uses a new feature of EF5 on .NET 4.5 which allows a property to be set as not modified even after it has been previously set to modified.
When using EF 4.3.1 or EF5 on .NET 4 you can do this instead:
var entry = context.Entry(obj);
foreach (var name in entry.CurrentValues.PropertyNames.Except(excluded))
{
entry.Property(name).IsModified = true;
}

You can't define such an exception. You can however mark single properties as modified:
context.Entry(obj).Property(o => o.Property3).IsModified = true;
context.Entry(obj).Property(o => o.Property4).IsModified = true;
// etc.
Note that setting IsModified to false is not supported once you have marked the state of the whole entity to Modified.
For your purpose I would actually prefer to load the entity from the database and then update it using normal change tracking:
var objInDB = context.Objects.Single(o => o.Id == obj.Id);
obj.Property1 = objInDB.Property1;
obj.Property2 = objInDB.Property2;
context.Entry(objInDB).CurrentValues.SetValues(obj);
context.SaveChanges();
Note that only changed properties will be saved by default by Automatic Detect changes.
See EF 6 and EF Core articles

This question was already nicely answered, but I wanted to provide an extension method for anyone who would like to use it.
This code was developed for EF 4.3.1
//You will need to import/use these namespaces
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
//Update an entity object's specified columns, comma separated
//This method assumes you already have a context open/initialized
public static void Update<T>(this DbContext context, T entityObject, params string[] properties) where T : class
{
context.Set<T>().Attach(entityObject);
var entry = context.Entry(entityObject);
foreach(string name in properties)
entry.Property(name).IsModified = true;
context.SaveChanges();
}
Usage Example
using (FooEntities context = new FooEntities())
{
FooEntity ef = new FooEntity();
//For argument's sake say this entity has 4 columns:
// FooID (PK), BarID (FK), Name, Age, CreatedBy, CreatedOn
//Mock changes
ef.FooID = 1;
ef.Name = "Billy";
ef.Age = 85;
context.Update<FooEntity>(ef, "Name", "Age"); //I only want to update Name and Age
}

This is an update that works for .net CORE and maybe can help someone who needs a generic solucion and wants to exclude some properties base on different conditions.
I'm using reflection to iterate through the properties and update base on its property value, in this case, as example, i'm excluding the null properties.
public virtual TEntity Update(TEntity entity)
{
dbSet.Attach(entity);
dbContext.Entry(entity).State = EntityState.Modified;
var entry = dbContext.Entry(entity);
Type type = typeof(TEntity);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.GetValue(entity, null) == null)
{
entry.Property(property.Name).IsModified = false;
}
}
dbContext.SaveChanges();
return entity;
}

The answers above (most of them) use DbContext. For those who is using ObjectContext these solutions arent accessible.
Here is solution for ObjectContext strictly (EF5 .NET 4.5):
ctx.AddObject("ENTITYNAME", item);
ctx.ObjectStateManager.ChangeObjectState(item, EntityState.Modified);
var entry = ctx.ObjectStateManager.GetObjectStateEntry(item);
entry.RejectPropertyChanges("PROPERTY_TO_EXCLUDE");

Related

efcore change modified state to update entity is not working with sub data objects

I set up a Generic repository using this code for update
private void AttachIfNot(TEntity entityToActive)
{
if (_dbContext.Entry(entityToActive).State == EntityState.Detached)
{
_dbSet.Attach(entityToActive);
}
}
private void UpdateEntity(TEntity entityToUpdate)
{
AttachIfNot(entityToUpdate);
_dbContext.Entry(entityToUpdate).State = EntityState.Modified;
}
It just attach the entity and set the modified state to save.
But when I use efocre ownsone to map a value object,the update entity function is not working.
I found out that it only works when I set Valueobject to modified too.
_dbContext.Entry(entityToUpdate).State = EntityState.Modified;
_dbContext.Entry(entityToUpdate.Valueobject).State = EntityState.Modified;
But It is hard for me to specify all the value objects in a Generic Repository.
This is code also has problems with one to many or other relations.
The working way is like this:
Classroom classroom = new Classroom
{
Id = 1,
Name = "b",
Students = new List<Student>
{
new Student()
{
Name = "aa",
Id = 2
}
}
};
if (_defaultDbContext.Entry(classroom).State == EntityState.Detached)
{
_defaultDbContext.Classrooms.Attach(classroom);
foreach(var stu in classroom.Students)
{
_defaultDbContext.Students.Attach(stu);
}
}
_defaultDbContext.Entry(classroom).State = EntityState.Modified;
foreach (var stu in classroom.Students)
{
_defaultDbContext.Entry(stu).State = EntityState.Modified;
}
_defaultDbContext.SaveChanges();
I found out one way is get the entity form repo then update it using automapper:
targetEntity = repo.GetById(entityId);
automapper.map(souceEntity,targetEntity);
//or
automapper.map(souceDto,targetEntity);
_dbContext.Save();
The entity comes by query, so the change will be tracked.
But I have to configure the automapper with this entity map when I want to change entity
CreateMap<EntityType, EntityType>();
I think it's not the best solution. Is there a bettere way?
DbContext.Update would be fine to fix this problem.
see:
https://www.learnentityframeworkcore.com/dbcontext/change-tracker

Clear related items followed by parent entity update - Many to Many EntityFramework with AutoMapper

I am trying to remove all references followed by adding them back from a list of disconnected objects.
using(var scope = new TransactionScope())
{
_autoIncidentService.AddNewCompanyVehicles(
autoIncidentModel.CompanyVehiclesInvolved.Where(v => v.Id == 0));
_autoIncidentService.ClearCollections(autoIncidentModel.Id);
_autoIncidentService.Update(autoIncidentModel);
scope.Complete();
}
return Json(ResponseView);
The ClearCollections removes items references. The GetAutoIncident includes the collection.
public void ClearCollections(int id)
{
var autoIncident = GetAutoIncident(id);
foreach (var vehicle in autoIncident.CompanyVehiclesInvolved.ToArray())
{
autoIncident.CompanyVehiclesInvolved.Remove(vehicle);
}
db.SaveChanges();
}
When I try to update the entity right after the ClearCollections method it fails.
The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.
I am using a singleton to get the DbContext so there shouldn't be any situation where the context is different. It is being stored in the HttpContext.Current.Items.
The update method is as follows:
public override void Update(AutoIncidentModel model)
{
var data = GetData(model.Id);
Mapper.CreateMap<AutoIncidentModel, AutoIncident>()
.ForMember(m => m.CompanyVehiclesInvolved, opt => opt.ResolveUsing(m =>
{
var ids = m.CompanyVehiclesInvolved.Select(v => v.Id);
return db.Vehicles.Where(v => ids.Contains(v.Id)).ToList();
}));
Mapper.Map(model, data);
db.SaveChanges();
}
Obviously, I am missing something important here. Do the entites from my ResolveUsing method need to somehow be associated with the parent entity or is automapper overwriting the property (CompanyVehiclesInvolved) and causing a problem?

How to ensure proxies are created when using the repository pattern with entity framework?

I have this method in my SurveyController class:
public ActionResult AddProperties(int id, int[] propertyids, int page = 1)
{
var survey = _uow.SurveyRepository.Find(id);
if (propertyids == null)
return GetPropertiesTable(survey, page);
var repo = _uow.PropertySurveyRepository;
propertyids.Select(propertyid => new PropertySurvey
{
//Setting the Property rather than the PropertyID
//prevents the error occurring later
//Property = _uow.PropertyRepository.Find(propertyid),
PropertyID = propertyid,
SurveyID = id
})
.ForEach(x => repo.InsertOrUpdate(x));
_uow.Save();
return GetPropertiesTable(survey, page);
}
The GetPropertiesTable redisplays Properties but PropertySurvey.Property is marked virtual and I have created the entity using the new operator, so a proxy to support lazy loading was never created and it is null when I access it. When we have access direct to the DbContext we can use the Create method to explicitly create the proxy. But I have a unit of work and repository pattern here. I guess I could expose the context.Create method via a repository.Create method and then I need to remember to use that instead of the new operator when I add an entity . But wouldn't it be better to encapsulate the problem in my InsertOrUpdate method? Is there some way to detect that the entity being added is not a proxy when it should be and substitute a proxy? This is my InsertOrUpdate method in my base repository class:
protected virtual void InsertOrUpdate(T e, int id)
{
if (id == default(int))
{
// New entity
context.Set<T>().Add(e);
}
else
{
// Existing entity
context.Entry(e).State = EntityState.Modified;
}
}
Based on the answer supplied by qujck. Here is how you can do it without having to employ automapper:
Edited to always check for proxy - not just during insert - as suggested in comments
Edited again to use a different way of checking whether a proxy was passed in to the method. The reason for changing the technique is that I ran into a problem when I introduced an entity that inherited from another. In that case an inherited entity can fail the entity.e.GetType().Equals(instance.GetType() check even if it is a proxy. I got the new technique from this answer
public virtual T InsertOrUpdate(T e)
{
DbSet<T> dbSet = Context.Set<T>();
DbEntityEntry<T> entry;
if (e.GetType().BaseType != null
&& e.GetType().Namespace == "System.Data.Entity.DynamicProxies")
{
//The entity being added is already a proxy type that supports lazy
//loading - just get the context entry
entry = Context.Entry(e);
}
else
{
//The entity being added has been created using the "new" operator.
//Generate a proxy type to support lazy loading and attach it
T instance = dbSet.Create();
instance.ID = e.ID;
entry = Context.Entry(instance);
dbSet.Attach(instance);
//and set it's values to those of the entity
entry.CurrentValues.SetValues(e);
e = instance;
}
entry.State = e.ID == default(int) ?
EntityState.Added :
EntityState.Modified;
return e;
}
public abstract class ModelBase
{
public int ID { get; set; }
}
I agree with you that this should be handled in one place and the best place to catch all looks to be your repository. You can compare the type of T with an instance created by the context and use something like Automapper to quickly transfer all of the values if the types do not match.
private bool mapCreated = false;
protected virtual void InsertOrUpdate(T e, int id)
{
T instance = context.Set<T>().Create();
if (e.GetType().Equals(instance.GetType()))
instance = e;
else
{
//this bit should really be managed somewhere else
if (!mapCreated)
{
Mapper.CreateMap(e.GetType(), instance.GetType());
mapCreated = true;
}
instance = Mapper.Map(e, instance);
}
if (id == default(int))
context.Set<T>().Add(instance);
else
context.Entry(instance).State = EntityState.Modified;
}

How can I update my DTO's ID when inserting multiple new entities

I'm using EF4. I'm adding a series of new entities from a list of DTOs, and I'm not saving changes until after all of them are added. I'm wanting to set the IDs of the DTOs to what the new entities' IDs are. How on earth do I do this? Does EF provide a mechanism for this?
With a single entity I would do this:
public void InsertMyDto(MyDto a_dto)
{
var newEntity = new MyEntity
{
Name = a_dto.Name,
Type = a_dto.Type.ToString(),
Price = a_dto.Price
};
_dataContext.MyEntities.AddObject(newEntity);
_dataContext.SaveChanges();
a_dto.ID = newEntity.ID;
}
This works fine, but what do I do in this case?
public void InsertMyDtos(IEnumerable<MyDto> a_dtos)
{
foreach (var myDto in a_dtos)
{
var newEntity = new MyEntity
{
Name = myDto.Name,
Type = myDto.Type.ToString(),
Price = myDto.Price
};
// Does some validation logic against the database that might fail.
_dataContext.MyEntities.AddObject(newEntity);
}
_dataContext.SaveChanges();
// ???
}
I want to save all at once, because I have validation work (not shown above) that is done against the database and fails before it gets to SaveChanges, and if it fails I want it to fail as a whole transaction (i.e. rollback).
I don't think that EF can help you here. It even can't help you for a single instance which forces you to write a_dto.ID = newEntity.ID. The counterpart of this code for multiple entites is to keep track of the pairs of dtos and new entities:
public void InsertMyDtos(IEnumerable<MyDto> a_dtos)
{
Dictionary<MyDto, MyEntity> dict = new Dictionary<MyDto, MyEntity>();
foreach (var myDto in a_dtos)
{
var newEntity = new MyEntity
{
Name = myDto.Name,
Type = myDto.Type.ToString(),
Price = myDto.Price
};
dict.Add(myDto, newEntity);
// Does some validation logic against the database that might fail.
_dataContext.MyEntities.AddObject(newEntity);
}
_dataContext.SaveChanges();
foreach (var item in dict)
item.Key.ID = item.Value.ID; // Key is MyDto, Value is MyEntity
}

Update entity framework objects

I transfer data between the entity framework and the business layer and user layer by using Data Transfer Objects. I do have some doubt, if I retrieve an object which is converted to a DTO, how do I update the correct object in the entity framework and not just insert a duplicate?
The following code will update an EF 4 entity that has been created as a controller parameter in MVC from a strongly typed view:
It seems the trick is to use the ObjectStateManager to change the state from Added to Modified once the entity has been added to the context.
MyEntities db = new MyEntities();
db.Product.AddObject(product);
db.ObjectStateManager.ChangeObjectState(product, System.Data.EntityState.Modified);
return db.SaveChanges() > 0;
As per #Sean Mills comment if you are using EF5 use:
((IObjectContextAdapter) db).ObjectContext.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Added);
an old question, but just in case someone needs a code solution:
http://www.mikesdotnetting.com/Article/110/ASP.NET-MVC-Entity-Framework-Modifying-One-to-Many-and-Many-to-Many-Relationships
Example:
public void EditArticle(
Article article, string articleTypeId, string[] categoryId)
{
var id = 0;
Article art = de.ArticleSet
.Include("ArticleTypes")
.Include("Categories")
.Where(a => a.ArticleID == article.ArticleID)
.First();
var count = art.Categories.Count;
for (var i = 0; i < count; i++)
{
art.Categories.Remove(art.Categories.ElementAt(i));
count--;
}
foreach (var c in categoryId)
{
id = int.Parse(c);
Category category = de.CategorySet
.Where(ct => ct.CategoryID == id).First();
art.Categories.Add(category);
}
art.Headline = article.Headline;
art.Abstract = article.Abstract;
art.Maintext = article.Maintext;
art.DateAmended = DateTime.Now;
art.ArticleTypesReference.EntityKey = new EntityKey(
"DotnettingEntities.ArticleTypeSet",
"ArticleTypeID",
int.Parse(articleTypeId)
);
de.SaveChanges();
}
//I am replacing player :)
public ActionResult ProductEdit(string Id, Product product)
{
int IdInt = DecyrptParameter(Id);
MyEntities db = new MyEntities();
var productToDetach = db.Products.FirstOrDefault(p=> p.Id == IdInt);
if (product == null)
throw new Exception("Product already deleted"); //I check if exists, maybe additional check if authorised to edit
db.Detach(productToDetach);
db.AttachTo("Products", product);
db.ObjectStateManager.ChangeObjectState(product, System.Data.EntityState.Modified);
db.SaveChanges();
ViewData["Result"] = 1; // successful result
return View();
}
You would need to include a primary or alternate key in the DTO, then match that key back to the correct EF entity upon update.
This should work for EF 5: https://stackoverflow.com/a/11749716/540802:
db.Entry(product).State = EntityState.Modified;