EntityCollection - ICollection - Runtime Issue/Quirk

c# entity-framework entity-framework-6 interface t4

Question

Detailed Information

I'm employing a database-first strategy.

To have the generated classes implement those interfaces, I updated the original TextTemplate file included/produced by the EDMX file (project item) and built a TextTemplate (.tt) file that produces interfaces from the EDMX file.

[Person] (Partial Public 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 demonstrated by the code example above, in order to initialize the Person class, I additionally added a constructor that accepts the interface.

[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; }
    }
}

The CrewMember Rank andICrewMember Inteface include:

  • boolean attributes (IsCaptain and IsAssigned )
  • Int PersonID as a possession,Int CrewMemberID as a possession,
  • and Person Person as a Person, naturally. (I've tried.)IPerson Person along with).

My Goal; The Problem

I was going to useICollection<ICrewMember> to aid in navigation. Utilize DTO, Model, and ModelView objects and classes that implement those interfaces.

When I producedICollection<InterfaceTEntity> Up until I attempted to update two Entities using the same context, I wasn't experiencing any problems with EntityFramework.context.CrewMembers and context.People within the same capacity.

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 Change-Tracking Feature of EntityFramework appears to be the problem I am seeing or witnessing. However, EntityFramework's own asynchronous operations serve as its true foundation.

If I walk through the aforementioned technique while debugging, the database and tables update as expected (No one is exempt from this).

It will throw an exception (a deceptive exception regarding Person) if I execute it without catching a debug point prior to the statement defining queryPerson. Crew Membership requirementsICollection<T> )..

Clearly, there is a time issue here.


Possible Workarounds/Interesting Locations

I tried removing the virtual attribute from the ICollection> properties, but that had no effect on the problem.

Lazy Loading was removed, but it had no impact on the problem.

I made an attempt to commit the modifications in between the queries, but it had no effect.

I made an effort to use.FirstAsync<> I'm not even sure if I'm using it correctly, to be honest. I'm continuing experimenting with this strategy.


(Misleading) Exception, Stack Trace, and Target Site *EDIT/UPDATE

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)}

*UPDATE/EDIT - A query

Although asking this question might not be the best way to frame the problem I'm facing...

How can I handle possibly dangerous threading instances (in my code) when utilizing Generic Type interfaces with the ICollection in Navigation Properties of the produced classes?UpdateCrewmMemberModel ) that are used by/from EntityFramework?

1
5
2/16/2015 9:53:36 PM

Accepted Answer

ZZZ_tmp
4
9/15/2014 5:49:50 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow