I have a model class which contains 5 properties but only 3 properties are shown in the view.
When I call create action, I need to update just this 3!!
I'm trying this:
[HttpPost]
public ActionResult Create([Bind(Exclude = "Id")]Location location)
{
if (ModelState.IsValid)
{
db.Location.Add(location);
bool car_in_database = db.Car.Any(c => c.Id == location.Car.Id);
if (car_in_database)
{
db.Entry(location.Car).State = EntityState.Modified;
db.Entry(location.Car).Property(l => l.ClientId).IsModified = false;
}
db.SaveChanges();
return RedirectToAction("Index");
}
but it doesn't work.. anyone know why or what I have to do?
Only .NET 4.5 and EF 5.0 support unmarking property as not modified so if you are using .NET 4.0 you must not set entity state to modified. Instead you must manually set each property you want to modify with IsModified = true
Related
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
In my EF 6 MVC app, I have an entity Seller which has a 1:1 relationship to SellerShippingPolicies. When I update the seller entity, EF is also attempting to update the SellerShippingPolicies entity, and I don't want this to happen.
I have the following method that updates a Seller entity:
public Entities.Seller Save(Entities.Seller seller)
{
// Instantiate a helper method
HelperMethods helper = new HelperMethods(this.UnitOfWork);
// Map the domain entity to an EF entity
var sellerRecord = Mapper.Map<Seller>(seller);
// Attempt to prevent the updating of the SellerShippingPolicies entity
helper.GetDbContext().Entry(sellerRecord.SellerShippingPolicies).State = EntityState.Detached;
// Save the entity
sellerRecord = helper.SaveItem<Seller>(sellerRecord);
}
Here is the SaveItem method that gets called:
public T SaveItem(T entity)
{
var row = this._dbSet.Find(GetPrimaryKeyValue(entity));
if ( row == null )
return AddItem(entity);
else
return UpdateItem(entity);
}
And the Update method that eventually gets called:
public T UpdateItem(T entity)
{
// Retrieve the current copy of the entity to be updated.
var currentEntity = GetItem(GetPrimaryKeyValue(entity));
// Copy the contents of the modified entity on top of the copy we just retrieved. This way EF will save everything correctly.
currentEntity = Copy.ShallowCopy<T>(entity, currentEntity);
this._dbContext.SaveChanges();
return currentEntity;
}
Not sure it's necessary, but here is the method for ShallowCopy and GetItem.
public static T ShallowCopy<T>(object source, T target)
{
foreach (PropertyInfo pi in typeof(T).GetProperties())
{
var property = source.GetType().GetProperty(pi.Name);
if (property == null)
continue;
if (property.GetSetMethod() != null)
property.SetValue(target, pi.GetValue(source, null), null);
}
return target;
}
public T GetItem(object primaryKeyValue)
{
return this._dbSet.Find(primaryKeyValue);
}
All these methods share the same context object.
You can see that I'm attempting to prevent the updating of the SellerShippingPolicies entity by setting its state to detached. This does not work. I've also tried setting the state to Unchanged. That doesn't work either. In both cases, EF attempts to update the SellerShippingPolicies entity. What am I missing?
i'm using SQLite Provider with Entity Frame work in .Net 4.5
conferControlEntities.Configuration.LazyLoadingEnabled = true;
conferControlEntities.Configuration.ProxyCreationEnabled = true;
conferControlEntities.Configuration.AutoDetectChangesEnabled = true;
conferControlEntities.Configuration.ValidateOnSaveEnabled = true;
and i am also using inheritance in the Entity Framework
so i cannot use the include in the query as i can't include the derived objects navigation property
so i have fillNavigationPropty method in Poco classes which will make a call to the Navigation properties and try to fill them
but whenever i check it will be null
using (var context = GetContext(false,true))
{
var queryable = context.Delegates.Where(i => i.ParentId == id);
foreach (var entity in queryable)
{
entity.FillNavigationProperties();
list.Add(WrapperFactory.Create(entity) as TU);
}
return new ObservableCollection<TU>(list);
}
POCO Example
public class TempPOCO
{
public virtual AnotherPCOCCLassTYpe NavigationProperty1{get;set;}
public void FillNavigationProperties()
{
var tempcall = NavigationProperty1;
}
}
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;
}
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");