work unit pattern mvc generic framework antipattern entity-framework linq-to-entities ef-code-first repository-pattern

entity-framework - unit - repository pattern obsolete



Entity Framework. Donde anidó.Include (1)

Estoy intentando realizar una búsqueda de db utilizando primero el código EF5. La estructura básica y las relaciones de tabla son las siguientes;

public partial class Member { public int RecordID {get; set;} public string Name {get; set;} ...etc. public virtual ICollection<MemberLink> MasterLinks {get; set;} public virtual ICollection<MemberLink> SlaveLinks {get; set;} public virtual ICollection<Message> ReceivedMessages {get; set;} public virtual ICollection<Message> SentMessages {get; set;} } public partial class MemberLink { public int RecordID {get; set;} public virtual Member MasterMember {get; set;} public virtual Member SlaveMember {get; set;} ...etc. } public partial class Message { public int RecordID {get; set;} public virtual Member Sender {get; set;} public virtual Member Recipient {get; set;} ...etc. }

Ahora, la consulta que estoy tratando de realizar está usando el MemberLinkRepository , y se ve como;

public IList<MemberLink> GetMasterLinks(int p_MemberID) { return Get() .Include ( memberLink => memberLink.MasterMember ) .Include ( memberLink => memberLink.SlaveMember ) .Include ( memberLink => memberLink.MasterMember.ReceivedMessages .Where( msg => msg.Sender.RecordID == memberLink.SlaveMember.RecordID) ) .Where ( memberLink => memberLink.MasterMember.RecordID == p_MemberID) .ToList();

Excepto que a EF no parece gustarle el lugar anidado. Podría dividir esto en 2 llamadas de repositorio separadas (y de hecho, parece que tengo que hacer eso) pero con el interés de reducir las llamadas a la base de datos estoy tratando de hacerlo de una sola vez. ¿Alguien sabe cómo puedo lograr esto en una sola consulta?

Espero que el código ilustre lo que trato de hacer ... Si no, lo intentaré y explicaré un poco mejor.


La respuesta corta es no, EF no le permitirá hacer eso usando Include() .

Piense en el resultado si le permite hacer esto: en un caso, su MemberLink.MasterMember.ReceivedMessages estará lleno, en otro objeto idéntico MemberLink.MasterMember.ReceivedMessages es en realidad un subconjunto de mensajes. ¿Qué sucede si intentas agregar a ReceivedMessages? ¿Qué pasa si la adición no coincide con el filtro? Es una bolsa de daño.

La respuesta es usar proyecciones:

public IList<MemberLinkWithFiltereredMessages> GetMasterLinks(int p_MemberID) { return Get() .Include(memberLink => memberLink.MasterMember) .Include(memberLink => memberLink.SlaveMember) .Where(memberLink => memberLink.MasterMember.RecordID == p_MemberID) .Select(memberLink => new MemberLinkWithFilteredMessages { MemberLink = memberLink, FilteredMessages = memberLink.MasterMember.ReceivedMessages .Where(msg => msg.Sender.RecordID == memberLink.SlaveMember.RecordID) }) .ToList(); }

Lo que realmente está haciendo es pedir un subconjunto de información específico, así que sea explícito al respecto.