EntityCollection - ICollection <InterfaceTEntity> - Runtime Error / Quirk

Background Information

I am using the Database-First approach.

I created a TextTemplate (.tt) file that generates the interfaces from the EDMX file, I also modified the original TextTemplate file that was added / created by the EDMX file (project element) so that the generated classes implement these interfaces.

[Face] (Partial Open Class)

namespace TestSolution.Domain.Entities { using System; using System.Collections.Generic; using TestSolution.Domain.Entities; public partial class Person : IPerson { public Person() { //this.CrewMembers = new HastSet<CrewMember>(); this.CrewMembers = new HashSet<ICrewMember>(); } public Person(IPerson iPerson) { this.PersonID = iPerson.PersonID; this.First = iPerson.First; this.Last = iPerson.Last; //this.CrewMembers = new HastSet<CrewMember>(); this.CrewMembers = new HashSet<ICrewMember>(); } public int PersonID { get; set; } public string First { get; set; } public string Last { get; set; } //public virtual ICollection<CrewMember> CrewMembers { get; set; } public virtual ICollection<ICrewMember> CrewMembers { get; set; } } } 

As you can see in the code example above , I also added a constructor that takes an interface to initialize the Person class ...

[IPerson] (interface)

 namespace TestSolution.Domain.Entities { using System; using System.Collections.Generic; public interface IPerson { int PersonID { get; set; } string First { get; set; } string Last { get; set; } //ICollection<CrewMember> CrewMembers { get; set; } ICollection<ICrewMember> CrewMembers { get; set; } } } 

CrewMember Class and ICrewMember Inteface contain:

  • boolean properties ( IsCaptain and IsAssigned )
  • Int PersonID as a property, Int CrewMemberID as a property,
  • and Person Person as Man, of course. (I also tried IPerson Person ).

My intention; Problem

I planned to use ICollection<ICrewMember> for navigation. Then use the DTO / Model / ModelView / class objects that implement these interfaces.

When I generated ICollection<InterfaceTEntity> types in the navigation property, I did not see any problems .. with EntityFramework .. until I tried to update two objects using the same context: context.CrewMembers and context.People in the same function.

 public void UpdateCrewMemberModel(CrewMemberModel CrewMember) { var query = (from crewMember in UnitOfWork.DataContext.CrewMembers where crewMember.CrewMemberID == CrewMember.CrewMemberID select crewMember).First<CrewMember>(); query.IsAssigned = CrewMember.IsAssigned; query.IsCaptain = CrewMember.IsCaptain; // Exception is thrown here var queryPerson = (from person in UnitOfWork.DataContext.People where person.PersonID == query.PersonID select person).First<Person>(); queryPerson.First = CrewMember.Person.First; queryPerson.Last = CrewMember.Person.Last; //Note that UnitOfWork uses a Factory Repository Pattern; //Commit just calls on UnitOfWork.DataContext.SaveAll() Method UnitOfWork.Commit(); } 

The problem I am facing / see seems to be related to the EntityFramework change tracking function. However, it is actually embedded in asynchronous operations inside the EntityFramework itself.

If I debug and execute the method described above, there is no exception , and the databases / tables are updated correctly.

If I run it without catching the debug point before the queryPerson declaration .. it will throw an exception (the misleading exception with respect to Person.CrewMembers should be ICollection<T> ) ..

This is definitely a problem of time.


Attempting a workaround / Points of Interest

I tried to remove the virtual property attribute ICollection <>; did not affect the problem.

I tried to remove Lazy Loading; did not affect the problem.

I tried to commit changes between requests; did not affect the problem.

I tried using FirstAsync<> on request; but I'm not sure that I even use it correctly. I still play with this approach.


* EDIT / UPDATE - (misleading) Exception, stack trace and target site

 System.Data.Entity.Core.EntityException {"The navigation property 'CrewMembers' on entity of type 'System.Data.Entity.DynamicProxies.Person_1A1EF42B1FC8D2DD0084F803201DE1DE4CF6E704C5AE129D954BD5BEAB55826C' must implement ICollection<T> in order for Entity Framework to be able to track changes in collections."} Source: EntityFramework at System.Data.Entity.Core.Objects.DataClasses.EntityCollection`1.CheckIfNavigationPropertyContainsEntity(IEntityWrapper wrapper) at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedTarget, Boolean applyConstraints, Boolean addRelationshipAsUnchanged, Boolean relationshipAlreadyExists, Boolean allowModifyingOtherEndOfRelationship, Boolean forceForeignKeyChanges) at System.Data.Entity.Core.Objects.DataClasses.RelatedEnd.Add(IEntityWrapper wrappedEntity, Boolean applyConstraints) at System.Data.Entity.Core.Objects.DataClasses.EntityReference`1.set_ReferenceValue(IEntityWrapper value) at System.Data.Entity.Core.Objects.DataClasses.EntityReference.SetEntityKey(EntityKey value, Boolean forceFixup) at System.Data.Entity.Core.Objects.EntityEntry.FixupEntityReferenceToPrincipal(EntityReference relatedEnd, EntityKey foreignKey, Boolean setIsLoaded, Boolean replaceExistingRef) at System.Data.Entity.Core.Objects.EntityEntry.FixupReferencesByForeignKeys(Boolean replaceAddedRefs, EntitySetBase restrictTo) at System.Data.Entity.Core.Objects.ObjectStateManager.FixupReferencesByForeignKeys(EntityEntry newEntry, Boolean replaceAddedRefs) at System.Data.Entity.Core.Objects.ObjectStateManager.AddEntry(IEntityWrapper wrappedObject, EntityKey passedKey, EntitySet entitySet, String argumentName, Boolean isAdded) at System.Data.Entity.Core.Common.Internal.Materialization.Shaper.HandleEntityAppendOnly[TEntity](Func`2 constructEntityDelegate, EntityKey entityKey, EntitySet entitySet) at lambda_method(Closure , Shaper ) at System.Data.Entity.Core.Common.Internal.Materialization.Coordinator`1.ReadNextElement(Shaper shaper) at System.Data.Entity.Core.Common.Internal.Materialization.Shaper`1.SimpleEnumerator.MoveNext() at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext() at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at TestSolution.Infrastructure.Service.CrewMemberService.UpdateCrewMemberModel(CrewMemberModel CrewMember) in c:\Users\brett.caswell\Documents\Visual Studio 2012\Projects\TestSolution\TestSolution.Infrastructure.Service\Services\CrewMemberServices.cs:line 67 TargetSite: {Boolean CheckIfNavigationPropertyContainsEntity(System.Data.Entity.Core.Objects.Internal.IEntityWrapper)} 

* EDIT / UPDATE - Question

Although asking this question may be the wrong definition of the question that I have ...

When using Generic Type interfaces with ICollection in the navigation properties of created classes, how can I handle potentially dangerous thread instances (in my UpdateCrewmMemberModel method) that occur inside / from EntityFramework?

+5
source share
1 answer

Usually you get this error if you try to use the interface as your object (which is not supported by EF). Is CrewMember really an interface?

If this is not an interface, but it is a class that derives from the interface, perhaps in some cases the EF may get confused. Perhaps you are doing some kind of free configuration? Maybe you are trying to map a base class or interface that would throw this error?

Your code is actually a bit confusing because you are supposed to use the β€œunit of work”, but you are accessing your dbcontext directly, therefore effectively bypassing UoW, but then calling Commit () on UoW .. I would suggest that you you probably need to rethink your data design here and probably get rid of UoW completely, since EF is already a unit of work.

+4
source

Source: https://habr.com/ru/post/1202601/


All Articles