I have my entity as:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I have my UserViewModel as
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
I am using these as below in my controller:
//This is called from my view via ajax
public void Save(UserViewModel uv)
{
// this throws error: cannot convert from UserViewModel to Entity.User
MyRepository.UpdateUser(uv);
}
My UpdateUser in repository class is as below:
public void UpdateUser(User u)
{
var user = GetUserDetails(u.Id);
user.Name = u.Name;
user.Address = u.Address;
//using entity framework to save
_context.SaveChanges();
}
How can I correctly map UserViewModel in my controller to my entity
By using AutoMapper you can do something like:
public void Save(UserViewModel uv)
{
// this throws error: cannot convert from UserViewModel to Entity.User
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<UserViewModel , User>();
});
User u = config.CreateMapper().Map<User>(uv);
MyRepository.UpdateUser(u);
}
Or manually :
public void Save(UserViewModel uv)
{
User u = new User()
{
Id = uv.Id
Name = uv.Name;
Address = uv.Address;
};
MyRepository.UpdateUser(u);
}
Which is not good to do it manually if you change your view-model and your model then you should change your code also, but with Automapper you don't need to change the code.
EDIT1:
This is not good idea to use model-view in repository (DataAccess Core) so it would be better to keep your public void UpdateUser(User u) and don't change it, in outside it is better to pass user to UpdateUser not UserViewModel like what you have done before.
EDIT2:
In my opinion non of answered posts doesn't related to SOC (Separation on concerns) even mine...
1- When I passed UserViewModel I've violated the SOC ....
2- In the other side if I got User in Peresentation layer directly I also violated the SOC.
I think the best way is a middle layer as proxy....
Presentation <----> Proxy <----> Repository.
Your repository deals with objects of type User, so you need to map the values back to an instance of that type and then make the call.
Assuming you have a method to get the user called GetUser:
public void Save(UserViewModel uv)
{
var user = MyRepository.GetUser(uv.Id);
user.Name = uv.Name;
user.Address = uv.Address;
MyRepository.UpdateUser(user);
}
You can then save the changes in your repository class. You can attach the object to make sure there are no issues if the object was created in a different context:
public void UpdateUser(User u)
{
_context.Users.Attach(u);
_context.Entry(u).State = EntityState.Modified;
_context.SaveChanges();
}
You are doing the mapping of property values(view model->enity model) inside your repositories UpdateUser method. So use the view model class (UserViewModel) as the parameter type of that.
public void UpdateUser(UserViewModel u)
{
// Get the entity first
var user = GetUserDetails(u.Id);
// Read the property values of view model object and assign to entity object
user.Name = u.Name;
user.Address = u.Address;
//using entity framework to save
_context.SaveChanges();
}
Now from your Save method ,you can pass the view model object to this method.
This will fix your compile time error (which is your current problem in the question), but be careful about what classes you are using in what layers. If you are too much worried about using a view model class in your data access layer, you can do that in a middle service layer. But then you are getting the entity model in that layer and doing the update there.
Remember, there is no definite answer for that question. Use the approach you think is readable and consistent with the project/ team. Often times, i tend to use the term "Common DTO classes" than "View models" so i can peacefully pass those around to another layer. I keep them in a separate project (called Common DTO) and this will be cross cutting across other projects. That means i will add a reference to this Common DTO project in my Web/UI layer and my data access/service layer and use those as needed.
Extremely basic question about best practice in MVC when binding drop down lists.
This inst a real world example but a basic example that explains my question:
Take the following model
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public virtual Car Car { get; set; }
}
public class Car
{
public int ID {get;set;}
public string Make {get; set;{}
public string Model {get; set;}
}
Then assume that these get flattened into a view model:
public class IndexViewModel
{
public int PersonID;
public string Name;
public int SelectedCarID;
public SelectList<Cars> Cars;
}
In my constructor I have an index method:
[HttpGet]
public ActionResult Index()
{
var person = _ctx.People.FirstOrDefault(x=>x.ID == 1);
var vm = new IndexViewModel(){
Name = person.Name,
SelectedCarID = person.Car.ID,
};
return View(vm);
}
Now, Assume that the person that is returned from the context has NO car record when the page first loads.
The view has a line :
#Html.DropDownListFor(m=>m.SelectedCarID, Model.Cars)
When the form is submitted it is picked up by the action :
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
var person = _ctx.People.FirstOrDefault(x=>x.ID == model.PersonID);
var car = _ctx.Cars.FirstOrDefault(x=>x.ID == model.SelectedCarID);
person.Name = model.name;
person.Car = car;
_ctx.SaveChanges();
}
Now that is the way I have done it for ages, I started using EF back when LINQ to SQL was taking off and I have always created my models like that as I was under the imperssion that it was the recommended way.
After a discussion with another developer today I am not sure if this is stil the best way? It has always irked me that I need to do a lookup against the database to get the Car record out just so that I can update the record.
My questions are:
What is the best way to achive what I have described above?
Is the above correct?
Is there a better way to update the car entity against the person without doing a lookup (Preferably without including the foreign keys in the model)?
Is it better to just include the FKs in the model (Its not the way Ive been doing it bit it seems more sensible)?
Is there a way to bind the drop down to the car object (The guy I spoke to seemed to suggest you could but my knowlege of MVC/asp.net and furious googling seems to indicate that you cant)?
This really ins't the place for Best Practices sort of questions (that would probably be Code Review).
However some notes initially.
Keep your domain objects in the domain
The first thing that stood out to me was the SelectList<Car> property. Where it appears as your Car entity is actually a domain entity. A domain entity should not be exposed to the UI for multiple reasons.
Entity framework proxy classes monitor changes to properties that can be inadvertently saved.
Re-factoring of domain entities requires re-factoring of UI Code.
Domain entities typically contact properties you would not like exposed or otherwise.
Serialization of the Domain Entities will also serialize navigation properties and (mostly likely) cause circular reference errors.
Your question
Given the above you know have your answer, you will have to do a lookup for an entity based on your criteria from your View Model. Your view model should not have any understanding of the data context. It is in fact a View Model not a Domain Entity. By telling your View Model to interact with your data contexts you have no separation between your Data Access layers and your Presentation layers.
Don't make your controller manage data access as well
Your controller has a lot of work to-do, managing data access shouldn't be one of them. Doing so you have infarct coupled your Presentation Layer with your Data Access layer. Now as this is an example its easy to forgive however re factoring your data access layer will have direct consequences to your Presentation layer. I would suggest places a Services layer in between your data access layer and the presentation layer.
Ok All this in practice how does it look.
This is my personal approach here but will look at decoupling the data layer from the Presentation layer, no domain objects passed to the Presentation layer and using services to broker the transactions to the data layer.
Sample Service
This service is responsible for handling the interaction between the data layer and presentation (note mock repositories).
public class SampleService
{
public SampleService()
{
_dbContext = new SampleContext();
}
readonly SampleContext _dbContext;
public virtual Person GetPersonById(int id)
{
return _dbContext.Persons.FirstOrDefault(x => x.ID == id);
}
public virtual Car GetCarById(int id)
{
return _dbContext.Cars.FirstOrDefault(x => x.ID == id);
}
public virtual IList<Car> GetAllCars()
{
return _dbContext.Cars.ToList();
}
public virtual void UpdatePerson(Person person)
{
if (person == null)
throw new ArgumentNullException(nameof(person));
_dbContext.SaveChanges();
}
public virtual void UpdateCar(Car car)
{
if (car == null)
throw new ArgumentNullException(nameof(car));
_dbContext.SaveChanges();
}
}
Does this appear to be more work, absolutely does but better to implement your service now than have to do it later. What we also achieve is one location to update if we wish to change any queries or interaction methods.
IndexViewModel
As we have agreed we are no longer passing the car object to the SelectList. Infact we only need to construct a basic IList<SelectListItem> and populate this from our controller.
public class IndexViewModel
{
public IndexViewModel()
{
AvailableCars = new List<SelectListItem>();
}
public int PersonID { get; set; }
public string Name { get; set; }
public int SelectedCarId { get; set; }
public IList<SelectListItem> AvailableCars { get; set; }
}
Controller
Now our controller is pretty simple to wire up.
[HttpGet]
public ActionResult Index()
{
var person = sampleService.GetPersonById(1);
var model = new IndexViewModel
{
Name = person.Name,
PersonID = person.ID,
SelectedCarId = person.Car.ID
};
model.AvailableCars = sampleService.GetAllCars()
.Select(car => new SelectListItem
{
Text = $"{car.Make} - {car.Model}",
Value = car.ID.ToString()
})
.OrderBy(sli => sli.Text)
.ToList();
return View(model);
}
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
var person = sampleService.GetPersonById(model.PersonID);
if(person != null)
{
person.Name = model.Name;
//only update the person car if required.
if(person.Car == null || person.Car.ID != model.SelectedCarId)
{
var car = sampleService.GetCarById(model.SelectedCarId);
if (car != null)
person.Car = car;
}
sampleService.UpdatePerson(person);
}
return View();
}
View Drop Down list
#Html.DropDownListFor(m => m.SelectedCarId, Model.AvailableCars)
If you compare your code to my code I have actually added more code to the solution, however removes a lot of coupling and dependencies that could become hard to manage in larger applications.
Now back to your original questions.
Is there a better way to update the car entity against the person without doing a lookup (Preferably without including the foreign keys
in the model)?
No, you should be doing a lookup for that entity (car) outside of the Model. The model should not be aware of the data context.
Is it better to just include the FKs in the model (Its not the way Ive been doing it bit it seems more sensible)?
NO, your model should not be aware of the data context, therefore you do not need to define foreign keys (in a data context sense) leave that to your controller and services.
Is there a way to bind the drop down to the car object (The guy I spoke to seemed to suggest you could but my knowlege of MVC/asp.net
and furious googling seems to indicate that you cant)?
You could, but you don't want to. Our Car entity is a domain entity and we dont want to expose the entity to the UI (Presentation). Instead we will use other classes to expose what properties are bound. In this example a simple IList<SelectListItem> was more than sufficient.
We are converting our project from ObjectContext to dbContext.
Our current problem is with the difference in how eager loading is handled.
Example context
public class Person
{
public virtual ICollection<Email> Emails { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Email
{
public string Address{ get; set; }
}
public class Post
{
public string Content{ get; set; }
}
we have many pieces of code throughout the enterprise that expect the emails to be loaded and therefore call person.Emails.First() without thinking about it.
So we need to make sure that Emails are eagerly loaded.
Sometimes we can just us Include
However, when we use projections in our data layer we are running into problems. i.e.
return context.Persons.Select(p=> new Top5VM {
Person = p,
TopPosts = p.Posts.Take(5)
};
We have a lot of code that relies on Top5VM and expects Person.Emails to be loaded.
No mater what we've tried we can not figure out where to put the Include (or Load) function call where it will actually make a difference .
With the ObjectContext we would just have a dummy property on the Top5VM called Emails. Once that was loaded, the ObjectContext had references to all of those Entities an therefore never needed to go back to the server even when we accessed them through the person object. But that no longer works with the DbContext
Figured it out. I need to set context.Configuration.LazyLoadingEnabled = false; any time I want to use projection to accomplish eager loading. Unless of course I want to access the loaded entities directly from the projection.
You can do this by always using your own DbContext in between:
public class MyDbContext : DbContext
{
public MyDbContext() : base()
{
Configuration.LazyLoadingEnabled = false;
}
}
Then use this everywhere instead of a DbContext.
Alternately, if you have control of all the POCOs, you could just remove the virtual keyword from the related collections ... the virtual keyword is the magic hookup Microsoft uses for lazy loading.
EDIT
I also tend to do this as an extension method:
static class DbContextExtensions
{
public static DbContext AsEagerLoadingContext(this IDbContext context)
{
context.Configuration.LazyLoadingEnabled = false;
//context.Configuration.AutoDetectChangesEnabled = false;
return context;
}
}
Used as so, allowing lazy loading to be used or not as needed:
using (var context = new DbContext().AsEagerLoadingContext())
context.Stuff.Select(s => s.AllTheThings);
I am very new to entity framework and I am having a problem with a web api based site (connected to mssql) that I am writing. I keep getting seemingly random errors (mostly seeming to be database related). These errors happen most often when the site is first published but they do sometimes happen when it has been hours since the last publish. A selection of the errors:
Invalid operation. The connection is closed.
There is already an open DataReader associated with this Command which must be closed first.
The connection was not closed. The connection's current state is connecting.
The context cannot be viewed while the model is being created
Underlying provider failed to open
My context looks like this:
public class Context : DbContext
{
public Context() : base("name=DefaultConnection")
{
}
public override int SaveChanges()
{
DateTime now = DateTime.Now;
foreach (ObjectStateEntry entry in (this as IObjectContextAdapter).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified))
{
if (!entry.IsRelationship)
{
IHasUpdated updated = entry.Entity as IHasUpdated;
if (updated != null)
updated.updated = now;
}
}
return base.SaveChanges();
}
public DbSet<Branch> Branches { get; set; }
public DbSet<Company> Companies { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UsefulLink> UsefulLinks { get; set; }
}
There are many more DbSets than this. Should I be creating a separate context for each?
One of my basic controllers:
public class UsefulLinksController : ApiController
{
private Context db = new Context();
[ResponseType(typeof(UsefulLinksWrapper))]
public IHttpActionResult GetUsefulLinks([FromUri]UsefulLinkParams prams)
{
UsefulLinksWrapper wrapper = new UsefulLinksWrapper();
Meta meta = new Meta();
IQueryable<UsefulLink> query = db.UsefulLinks;
if (prams.sortBy == null)
{
prams.sortBy = "ID";
}
// Paging
query = query.OrderBy(prams.sortBy + " " + prams.sortDirection).Skip(prams.offset - 1).Take(prams.limit);
List<UsefulLink> data = query.ToList();
meta.totalCount = query.Count();
meta.offset = 1;
meta.limit = prams.limit;
wrapper.meta = meta;
wrapper.data = data;
return Ok(wrapper);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool UsefulLinkExists(int id)
{
return db.UsefulLinks.Count(e => e.ID == id) > 0;
}
}
I don't seem to see these errors when I run the site locally though there are two of us hitting it when it is published so perhaps the issue stems from multiple users?
Chris, I notice in your controller you are sharing your db context with all of the methods in your controller class.
This is generally not a best practice in Entity Framework (see: EntityFramework 4 ObjectContext Lifetime). You should keep your context alive as briefly as possible. Leaving the context alive to share across multiple methods could result in many of the errors that you list above.
I would recommend trying to instantiate a new instance of the context, instead, wherever it is used and quickly disposing of it.
This should generally result in more stable behavior.
So the below:
class SomeClass
{
private context = new Context(); //sharing your context with all methods
public someMethod()
{
context.doSomething;
}
public someMethod2()
{
context.doSomething;
}
}
should become:
class SomeClass
{
public someMethod()
{
Context context = new Context(); //now your context is declared and disposed of within each method
context.doSomething;
}
public someMethod2()
{
Context context = new Context(); //now your context is declared and disposed of within each method
context.doSomething;
}
}
Or even better, you can use a using construct to ensure that your context is properly disposed of:
class SomeClass
{
public someMethod3()
{
using(Context context = new Context()) //now wrapping the context in a using to ensure it is disposed
{
context.doSomething;
}
}
}
I would recommend trying the above changes and seeing if your behavior becomes more stable.
Since I do not know how your page uses the methods UsefulLinksController and in which order, I would say UsefulLinkExists is perhaps the culprit due to lazy loading
Lazy loading means delaying the loading of related data until you
specifically request it
Which would explain why your "reader" remains "open".
Try:
return db.UsefulLinks.ToList().Count(e => e.ID == id) > 0;
In any case, you can disable lazy loading by default in the context constructor as such noted here:
public MyEntitiesContext() : base("name=MyEntitiesContext", "MyEntitiesContext")
{
this.ContextOptions.LazyLoadingEnabled = false;
OnContextCreated();
}
As far as I know, it applies to EF4 and up.
In a nutshell the exception is thrown during POSTing wrapper model and changing the state of one entry to 'Modified'. Before changing the state, the state is set to 'Detached' but calling Attach() does throw the same error. I'm using EF6.
Please find my code below(model names have been changed to make it easier to read)
Model
// Wrapper classes
public class AViewModel
{
public A a { get; set; }
public List<B> b { get; set; }
public C c { get; set; }
}
Controller
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
if (!canUserAccessA(id.Value))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
var aViewModel = new AViewModel();
aViewModel.A = db.As.Find(id);
if (aViewModel.Receipt == null)
{
return HttpNotFound();
}
aViewModel.b = db.Bs.Where(x => x.aID == id.Value).ToList();
aViewModel.Vendor = db.Cs.Where(x => x.cID == aViewModel.a.cID).FirstOrDefault();
return View(aViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(AViewModel aViewModel)
{
if (!canUserAccessA(aViewModel.a.aID) || aViewModel.a.UserID != WebSecurity.GetUserId(User.Identity.Name))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
if (ModelState.IsValid)
{
db.Entry(aViewModel.a).State = EntityState.Modified; //THIS IS WHERE THE ERROR IS BEING THROWN
db.SaveChanges();
return RedirectToAction("Index");
}
return View(aViewModel);
}
As shown above line
db.Entry(aViewModel.a).State = EntityState.Modified;
throws exception:
Attaching an entity of type 'A' failed because another entity of the
same type already has the same primary key value. This can happen when
using the 'Attach' method or setting the state of an entity to
'Unchanged' or 'Modified' if any entities in the graph have
conflicting key values. This may be because some entities are new and
have not yet received database-generated key values. In this case use
the 'Add' method or the 'Added' entity state to track the graph and
then set the state of non-new entities to 'Unchanged' or 'Modified' as
appropriate.
Does anybody see anything wrong in my code or understand in what circumstances it would throw such error during editing a model?
Problem SOLVED!
Attach method could potentially help somebody but it wouldn't help in this situation as the document was already being tracked while being loaded in Edit GET controller function. Attach would throw exactly the same error.
The issue I encounter here was caused by function canUserAccessA() which loads the A entity before updating the state of object a. This was screwing up the tracked entity and it was changing state of a object to Detached.
The solution was to amend canUserAccessA() so that the object I was loading wouldn't be tracked. Function AsNoTracking() should be called while querying the context.
// User -> Receipt validation
private bool canUserAccessA(int aID)
{
int userID = WebSecurity.GetUserId(User.Identity.Name);
int aFound = db.Model.AsNoTracking().Where(x => x.aID == aID && x.UserID==userID).Count();
return (aFound > 0); //if aFound > 0, then return true, else return false.
}
For some reason I couldnt use .Find(aID) with AsNoTracking() but it doesn't really matter as I could achieve the same by changing the query.
Hope this will help anybody with similar problem!
Interestingly:
_dbContext.Set<T>().AddOrUpdate(entityToBeUpdatedWithId);
Or if you still is not generic:
_dbContext.Set<UserEntity>().AddOrUpdate(entityToBeUpdatedWithId);
seems to solved my problem smoothly.
It seems that entity you are trying to modify is not being tracked correctly and therefore is not recognized as edited, but added instead.
Instead of directly setting state, try to do the following:
//db.Entry(aViewModel.a).State = EntityState.Modified;
db.As.Attach(aViewModel.a);
db.SaveChanges();
Also, I would like to warn you that your code contains potential security vulnerability. If you are using entity directly in your view model, then you risk that somebody could modify contents of entity by adding correctly named fields in submitted form. For example, if user added input box with name "A.FirstName" and the entity contained such field, then the value would be bound to viewmodel and saved to database even if the user would not be allowed to change that in normal operation of application.
Update:
To get over security vulnerability mentioned previously, you should never expose your domain model as your viewmodel but use separate viewmodel instead. Then your action would receive viewmodel which you could map back to domain model using some mapping tool like AutoMapper. This would keep you safe from user modifying sensitive data.
Here is extended explanation:
http://www.stevefenton.co.uk/Content/Blog/Date/201303/Blog/Why-You-Never-Expose-Your-Domain-Model-As-Your-MVC-Model/
Try this:
var local = yourDbContext.Set<YourModel>()
.Local
.FirstOrDefault(f => f.Id == yourModel.Id);
if (local != null)
{
yourDbContext.Entry(local).State = EntityState.Detached;
}
yourDbContext.Entry(applicationModel).State = EntityState.Modified;
for me the local copy was the source of the problem.
this solved it
var local = context.Set<Contact>().Local.FirstOrDefault(c => c.ContactId == contact.ContactId);
if (local != null)
{
context.Entry(local).State = EntityState.Detached;
}
My case was that I did not have direct access to EF context from my MVC app.
So if you are using some kind of repository for entity persistence it could be appropiate to simply detach explicitly loaded entity and then set binded EntityState to Modified.
Sample (abstract) code:
MVC
public ActionResult(A a)
{
A aa = repo.Find(...);
// some logic
repo.Detach(aa);
repo.Update(a);
}
Repository
void Update(A a)
{
context.Entry(a).EntityState = EntityState.Modified;
context.SaveChanges();
}
void Detach(A a)
{
context.Entry(a).EntityState = EntityState.Detached;
}
Use AsNoTracking() where you are getting your query.
var result = dbcontext.YourModel.AsNoTracking().Where(x => x.aID == aID && x.UserID==userID).Count();
I have added this answer only because the problem is explained based on more complex data pattern and I found it hard to understand here.
I created a fairly simple application. This error occurred inside Edit POST action. The action accepted ViewModel as an input parameter. The reason for using the ViewModel was to make some calculation before the record was saved.
Once the action passed through validation such as if(ModelState.IsValid), my wrongdoing was to project values from ViewModel into a completely new instance of Entity. I thought I'd have to create a new instance to store updated data and then saved such instance.
What I had realised later was that I had to read the record from database:
Student student = db.Students.Find(s => s.StudentID == ViewModel.StudentID);
and updated this object. Everything works now.
I thought I'd share my experience on this one, even though I feel a bit silly for not realising sooner.
I am using the repository pattern with the repo instances injected into my controllers. The concrete repositories instantiate my ModelContext (DbContext) which lasts the lifetime of the repository, which is IDisposable and disposed by the controller.
The issue for me was that I have a modified stamp and row version on my entities, so I was getting them first in order to compare with the inbound headers. Of course, this loaded and tracked the entity that was subsequently being updated.
The fix was simply to change the repository from newing-up a context once in the constructor to having the following methods:
private DbContext GetDbContext()
{
return this.GetDbContext(false);
}
protected virtual DbContext GetDbContext(bool canUseCachedContext)
{
if (_dbContext != null)
{
if (canUseCachedContext)
{
return _dbContext;
}
else
{
_dbContext.Dispose();
}
}
_dbContext = new ModelContext();
return _dbContext;
}
#region IDisposable Members
public void Dispose()
{
this.Dispose(true);
}
protected virtual void Dispose(bool isDisposing)
{
if (!_isDisposed)
{
if (isDisposing)
{
// Clear down managed resources.
if (_dbContext != null)
_dbContext.Dispose();
}
_isDisposed = true;
}
}
#endregion
This allows the repository methods to re-new their context instance upon each use by calling GetDbContext, or use a previous instance if they so desire by specifying true.
I had this problem with local var and i just detach it like this:
if (ModelState.IsValid)
{
var old = db.Channel.Find(channel.Id);
if (Request.Files.Count > 0)
{
HttpPostedFileBase objFiles = Request.Files[0];
using (var binaryReader = new BinaryReader(objFiles.InputStream))
{
channel.GateImage = binaryReader.ReadBytes(objFiles.ContentLength);
}
}
else
channel.GateImage = old.GateImage;
var cat = db.Category.Find(CatID);
if (cat != null)
channel.Category = cat;
db.Entry(old).State = EntityState.Detached; // just added this line
db.Entry(channel).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(channel);
Problem causes of loaded objects with same Key, so first we will detach that object and do the the updating to avoid conflict between two object with the same Key
i mange to fix the issue by updating state. when you trigger find or any other query operation on the same record sate has been updated with modified so we need to set status to Detached then you can fire your update change
ActivityEntity activity = new ActivityEntity();
activity.name="vv";
activity.ID = 22 ; //sample id
var savedActivity = context.Activities.Find(22);
if (savedActivity!=null)
{
context.Entry(savedActivity).State = EntityState.Detached;
context.SaveChanges();
activity.age= savedActivity.age;
activity.marks= savedActivity.marks;
context.Entry(activity).State = EntityState.Modified;
context.SaveChanges();
return activity.ID;
}
I had a similar issue, after probing for 2-3 days found ".AsNoTracking" should be removed as EF doesn't track the changes and assumes there are no changes unless an object is attached. Also if we don't use .AsNoTracking, EF automatically knows which object to save/update so there is no need to use Attach/Added.
I encountered this error where
two methods, A & B, in a single controller both used the same instance of an ApplicationDbContext, and
method A called method B
private ApplicationDbContext db;
// api methods
public JsonResult methodA(string id){
Resource resource = db.Resources.Find(id);
db.Entry(resource).State = EntityState.Modified;
db.SaveChanges();
return methodB()
}
public JsonResult methodB(string id){
Resource resource = db.Resources.Find(id);
db.Entry(resource).State = EntityState.Modified;
db.SaveChanges();
return new JsonResult();
}
I changed method B to have a using statement and rely only on the local db2.
After:
private ApplicationDbContext db;
// api methods
public JsonResult methodA(string id){
Resource resource = db.Resources.Find(id);
db.Entry(resource).State = EntityState.Modified;
db.SaveChanges();
return methodB()
}
public JsonResult methodB(string id){
using (var db2 = new ApplicationDbContext())
{
Resource resource = db2.Resources.Find(id);
db2.Entry(resource).State = EntityState.Modified;
db2.SaveChanges();
}
return new JsonResult();
}
Similar to what Luke Puplett is saying, the problem can be caused by not properly disposing or creating your context.
In my case, I had a class which accepted a context called ContextService:
public class ContextService : IDisposable
{
private Context _context;
public void Dispose()
{
_context.Dispose();
}
public ContextService(Context context)
{
_context = context;
}
//... do stuff with the context
My context service had a function which updates an entity using an instantiated entity object:
public void UpdateEntity(MyEntity myEntity, ICollection<int> ids)
{
var item = _context.Entry(myEntity);
item.State = EntityState.Modified;
item.Collection(x => x.RelatedEntities).Load();
myEntity.RelatedEntities.Clear();
foreach (var id in ids)
{
myEntity.RelatedEntities.Add(_context.RelatedEntities.Find(id));
}
_context.SaveChanges();
}
All of this was fine, my controller where I initialized the service was the problem. My controller originally looked like this:
private static NotificationService _service =
new NotificationService(new NotificationContext());
public void Dispose()
{
}
I changed it to this and the error went away:
private static NotificationService _service;
public TemplateController()
{
_service = new NotificationService(new NotificationContext());
}
public void Dispose()
{
_service.Dispose();
}
Here what I did in the similar case.
That sitatuation means that same entity has already been existed in the context.So following can help
First check from ChangeTracker if the entity is in the context
var trackedEntries=GetContext().ChangeTracker.Entries<YourEntityType>().ToList();
var isAlreadyTracked =
trackedEntries.Any(trackedItem => trackedItem.Entity.Id ==myEntityToSave.Id);
If it exists
if (isAlreadyTracked)
{
myEntityToSave= trackedEntries.First(trackedItem => trackedItem.Entity.Id == myEntityToSave.Id).Entity;
}
else
{
//Attach or Modify depending on your needs
}
I solve this problem with a "using" block
using (SqlConnection conn = new SqlConnection(connectionString))
{
// stuff to do with data base
}
// or if you are using entity framework
using (DataBaseEntity data = new DataBaseEntity)
{
}
Here is where I get the idea https://social.msdn.microsoft.com/Forums/sqlserver/es-ES/b4b350ba-b0d5-464d-8656-8c117d55b2af/problema-al-modificar-en-entity-framework?forum=vcses is in spanish (look for the second answer)
you can use added method like;
_dbContext.Entry(modelclassname).State = EntityState.Added;
but in many case if you want to use more than one model at that time this won't work because entity is already attached to another entity. So, at that time you can use ADDOrUpdate Entity Migration method which simply migrates object from one to another and as a result you wouldn't get any error.
_dbContext.Set<modelclassname>().AddOrUpdate(yourmodel);
Clear all State
dbContextGlobalERP.ChangeTracker.Entries().Where(e => e.Entity != null).ToList().ForEach(e => e.State = EntityState.Detached);
Reasons I've encountered this error:
Did not use .AsNoTracking() when querying for existing entities. Especially when calling a helper function to check permissions.
Calling .Include() on a query and then trying to edit the parent. Example: var ent = repo.Query<Ent>().Include(e=>e.Ent2).First(); ...repo.Edit(e.Ent2); repo.Edit(e); If I'm going to edit a nested object, I try to separate these into separate query calls now. If you can't do that, set the child object to null and iterate through lists, detaching objects like this
Editing an old entity in a Put web call. The new item is already added to the repo, so modify that one and have it be saved in super.Put(). Example of what will throw an error: public void Put(key, newItem){ var old = repo.Query<Entity>().Where(e=>Id==key).First(); ... repo.Edit(old); super.Put(key,newItem); ... }
Multiple helper functions edit the same entity. Instead of passing the ID as a parameter into each function, pass a reference to the entity. Error solved!
In my case , I had wrote really two times an entity of same type . So I delete it and all things work correctly
This problem may also be seen during ViewModel to EntityModel mapping (by using AutoMapper, etc.) and trying to include context.Entry().State and context.SaveChanges() such a using block as shown below would solve the problem. Please keep in mind that context.SaveChanges() method is used two times instead of using just after if-block as it must be in using block also.
public void Save(YourEntity entity)
{
if (entity.Id == 0)
{
context.YourEntity.Add(entity);
context.SaveChanges();
}
else
{
using (var context = new YourDbContext())
{
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges(); //Must be in using block
}
}
}