asp.net mvc - An entity object cannot be referenced by multiple instances of IEntityChangeTracker when saving changes -


i have 2 domain objects, both identical different pk properties:

public partial class maintenance : maintenancebase {     [key]     [databasegeneratedattribute(databasegeneratedoption.identity)]     public int maintenanceid { get; set; }      public virtual employee employee { get; set; } }  public partial class mymaintenance : maintenancebase {     [key]     [databasegeneratedattribute(databasegeneratedoption.identity)]     public int rowid { get; set; }      public virtual employee employee { get; set; } } 

the rest of properties inherited base class. problem having when try call save changes in post controller, getting following error:

an entity object cannot referenced multiple instances of ientitychangetracker 

this (basically) controller method:

    [httppost]     public actionresult submitmymaintenance(ilist<mymaintenance> mymaintenancelist, string username)     {         foreach (var result in mymaintenancelist)     {     var m = imymaintenancerepository.getsingle(result.rowid);     maintenance maintenance = new maintenance();      // use injector handle mapping between viewmodel , model     maintenance.injectfrom(m);      try     {         if (modelstate.isvalid)         {             // save maintenance item             imaintenancerepository.add(maintenance);             imaintenancerepository.save();              // delete item in mymaintenance             imymaintenancerepository.delete(m);             imymaintenancerepository.save();         }     }     catch (dataexception ex)     {         message = ex.innerexception.tostring();     } }  // refresh view  var mvm = new mymaintenancelistviewmodel {     mymaintenancelist = imymaintenancerepository.findby(v => v.createdby.equals(username)).tolist(),     message = "your maintenance items added." };  return view("mymaintenance", mvm);  } 

i suspect because have instances of respositories (imaintenancerepository & imymaintenancerepository) both domain objects in same controller post method, , both have reference employee entity.

for instance, when dispose imymaintenancerepository , create new instance (before refreshing view @ end), en error inserting null value in employee table, not inserting anything. reason suspect employee entity exists in 2 different data contexts. not sure how resolve though. none of solutions have found seem apply , thinking more of implementation problem on part.

edit: repositories

namespace emms.models.interfaces {     public interface imymaintenancerepository : igenericrepository<mymaintenance>     {         mymaintenance getsingle(int rowid);     } }  namespace emms.models.repositories {     public class mymaintenancerepository : genericrepository<appdbcontext, mymaintenance>, imymaintenancerepository     {         public mymaintenance getsingle(int rowid)         {             var query = getall().firstordefault(x => x.rowid == rowid);             return query;         }     } }  namespace emms.viewmodels.repositories {     public class genericrepository<c, t> : idisposable, igenericrepository<t>         t : class         c : dbcontext, new()     {         private c _entities = new c();         public c context         {             { return _entities; }             set { _entities = value; }         }          public virtual iqueryable<t> getall()         {             iqueryable<t> query = _entities.set<t>();             return query;         }          public iqueryable<t> findby(system.linq.expressions.expression<func<t, bool>> predicate)         {             iqueryable<t> query = _entities.set<t>().where(predicate);             return query;         }          // enforce referential itegrity         public bool valueinuse(system.linq.expressions.expression<func<t, bool>> predicate)         {             iqueryable<t> query = _entities.set<t>().where(predicate);             int count = query.count();             return count > 0 ? true : false;         }          public virtual void add(t entity)         {             _entities.set<t>().add(entity);         }          public virtual void delete(t entity)         {             _entities.entry(entity).state = system.data.entitystate.deleted;         }          public virtual void edit(t entity)         {             _entities.entry(entity).state = system.data.entitystate.modified;         }          public virtual void save()         {             _entities.savechanges();         }          private bool disposed = false; // detect redundant calls         protected virtual void dispose(bool disposing)         {             if (!disposed)             {                 if (disposing)                 {                     if (_entities != null)                     {                         _entities.dispose();                     }                 }                  disposed = true;             }         }          public void dispose()         {             dispose(true);             gc.suppressfinalize(this);         }     } }  namespace emms.viewmodels.interfaces {     public interface igenericrepository<t> t : class     {         iqueryable<t> getall();         iqueryable<t> findby(expression<func<t, bool>> predicate);         bool valueinuse(system.linq.expressions.expression<func<t, bool>> predicate);         void add(t entity);         void delete(t entity);         void edit(t entity);         void save();         void dispose();     } } 

you're absolute correct issue. actually, in particular, it's because each repository has it's own instance of context object, , you're trying pass employee retrieved via 1 instance , saving via different instance.

the easiest solution track things in 1 repository. in other words, use 1 maintenancerepository can have calls return both maintenance , mymaintenance. though stretches idea of "repository" bit. why repositories typically combined unit of work class, house context repositories share. however, @ point, you're recreating structure entity framework implements. so, holding in 1 "repository" makes more sense, you're talking "service" pattern rather repository pattern. it's semantics though.

update

disclaimer: i'm using in project , works me. may not best practice , reasonable people disagree approach.

iservice interface

public interface iservice<tcontext, tentity>     tcontext : dbcontext     tentity : class {     ienumerable<tentity> getall(         func<iqueryable<tentity>, iorderedqueryable<tentity>> orderby = null,         string includeproperties = "");     ienumerable<tentity> get(         expression<func<tentity, bool>> filter = null,         func<iqueryable<tentity>, iorderedqueryable<tentity>> orderby = null,         string includeproperties = "");     tentity getbyid(int id, string includeproperties = "");     tentity getone(         expression<func<tentity, bool>> filter = null,         string includeproperties = "");     tentity getfirst(         expression<func<tentity, bool>> filter = null,         func<iqueryable<tentity>, iorderedqueryable<tentity>> orderby = null,         string includeproperties = "");     tentity getlast(         expression<func<tentity, bool>> filter = null,         func<iqueryable<tentity>, iorderedqueryable<tentity>> orderby = null,         string includeproperties = "");     void create(tentity entity);     void update(tentity entity);     void delete(int id);     void delete(tentity entity);     int count(expression<func<tentity, bool>> filter = null);     bool any(expression<func<tentity, bool>> filter = null); } 

service, implementation of iservice

public class service<tcontext, tentity> : iservice<tcontext, tentity>     tcontext : dbcontext     tentity : class {     internal tcontext context;     internal dbset<tentity> dbset;      public service(tcontext context)     {         this.context = context;         this.dbset = context.set<tentity>();     }      public virtual ienumerable<tentity> getall(         func<iqueryable<tentity>, iorderedqueryable<tentity>> orderby = null,         string includeproperties = "")     {         return get(null, orderby, includeproperties);     }      public virtual ienumerable<tentity> get(         expression<func<tentity, bool>> filter = null,         func<iqueryable<tentity>, iorderedqueryable<tentity>> orderby = null,         string includeproperties = "")     {         iqueryable<tentity> query = dbset;          if (filter != null)         {             query = query.where(filter);         }          foreach (var includeproperty in includeproperties.split             (new char[] { ',' }, stringsplitoptions.removeemptyentries))         {             query = query.include(includeproperty);         }          if (orderby != null)         {             return orderby(query).tolist();         }         else         {             return query.distinct().tolist();         }     }      public virtual tentity getbyid(int id, string includeproperties = "")     {         return dbset.find(id);     }      public virtual tentity getone(         expression<func<tentity, bool>> filter,         string includeproperties = "")     {         return get(filter, null, includeproperties).singleordefault();     }      public virtual tentity getfirst(         expression<func<tentity, bool>> filter = null,         func<iqueryable<tentity>, iorderedqueryable<tentity>> orderby = null,         string includeproperties = "")     {         return get(filter, orderby, includeproperties).firstordefault();     }      public virtual tentity getlast(         expression<func<tentity, bool>> filter = null,         func<iqueryable<tentity>, iorderedqueryable<tentity>> orderby = null,         string includeproperties = "")     {         return get(filter, orderby, includeproperties).lastordefault();     }      public virtual void create(tentity entity)     {         dbset.add(entity);     }      public virtual void delete(int id)     {         var entity = getbyid(id);         delete(entity);     }      public virtual void delete(tentity entity)     {         if (context.entry(entity).state == entitystate.detached)         {             dbset.attach(entity);         }         dbset.remove(entity);     }      public virtual void update(tentity entity)     {         if (context.entry(entity).state == entitystate.detached)         {             dbset.attach(entity);         }         context.entry(entity).state = entitystate.modified;     }      public virtual int count(expression<func<tentity, bool>> filter = null)     {         return get(filter).count();     }      public virtual bool any(expression<func<tentity, bool>> filter = null)     {         return count(filter) > 0;     } } 

servicegroup, abstract container services

public abstract class servicegroup<tcontext> : idisposable     tcontext : dbcontext {     protected tcontext context;      public virtual void save()     {         try         {             context.savechanges();         }         catch (dbentityvalidationexception validationexception)         {             string validationerrormessage = dbentityvalidationmessageparser.geterrormessage(validationexception);             console.writeline(validationerrormessage);         }      }      #region disposable     private bool disposed = false;      protected virtual void dispose(bool disposing)     {         if (!this.disposed)         {             if (disposing)             {                 context.dispose();             }         }         this.disposed = true;     }      public virtual void dispose()     {         dispose(true);         gc.suppressfinalize(this);     }     #endregion } 

so, way use whenever want create collection of things work with, subclass servicegroup so:

public class sampleservice : servicegroup<mydbcontext> {     public sampleservice()     {         this.context = new mydbcontext();     }      private service<mydbcontext, somemodel> somemodels;     public service<mydbcontext, somemodel> somemodels     {                 {             if (somemodels == null)             {                 somemodels = new service<mydbcontext, somemodel>(context);             }             return somemodels;         }     }      private service<mydbcontext, anothermodel> anothermodels;     public service<mydbcontext, anothermodel> anothermodels     {                 {             if (anothermodels == null)             {                 anothermodels = new service<mydbcontext, anothermodel>(context);             }             return anothermodels;         }     }      // rinse , repeat  } 

this makes sure using same context instance. use it, do:

var service = new sampleservice();  somemodels = service.somemodels.getall(); 

Comments

Popular posts from this blog

c# - Send Image in Json : 400 Bad request -

javascript - addthis share facebook and google+ url -

ios - Show keyboard with UITextField in the input accessory view -