Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping EF Entities to ViewModels maintaining ICollection and lazy loading

This is something that has bugged me since the shift to EF 6. How do we now map collections through to view models such that mapping changes back are not painful using IEnumerables. Here is a code snippet below demonstrating my problem:

Entity - SS.Entity.Event

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<SS.Entity.User> Broadcasters { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<SS.Entity.User> Viewers { get; set; }

Model - SS.Model.Event

public virtual ICollection<SS.Model.User> Broadcasters { get; set; }
public virtual ICollection<SS.Model.User> Viewers { get; set; }

Mapping Back to Entity After Modification of Collection

Broadcasters = e.Broadcasters.Select(u => new SS.Entity.User
{
    Id = u.Id,
    SkypeId = u.SkypeId,
    Name = u.Name
}).ToList(), // THIS IS THE PROBLEM
Viewers = e.Viewers.Select(u => new SS.Entity.User
{
    Id = u.Id,
    SkypeId = u.SkypeId,
    Name = u.Name
}).ToList() // THIS IS THE PROBLEM

The problem with this is I cannot map an ICollection to another ICollection as Select produces an IEnumerable which makes mapping back properties to EF afterwards a pain as I have to recreate the collection or enumerate it to update it. I know I'm missing something obvious, I have updated the ICollection's to be virtual as outlined in other answers but it is unclear to me how this helps.

Any help would be greatly appreciated!

Gerard

like image 717
Gerard Wilkinson Avatar asked Aug 15 '16 23:08

Gerard Wilkinson


People also ask

How do I set lazy loading in Entity Framework?

Lazy loading means delaying the loading of related data, until you specifically request for it. When using POCO entity types, lazy loading is achieved by creating instances of derived proxy types and then overriding virtual properties to add the loading hook.

How do I stop my EF core from lazy loading?

Disable Lazy Loading To turn off lazy loading for a particular property, do not make it virtual. To turn off lazy loading for all entities in the context, set its configuration property to false.

Is lazy loading enabled by default in Entity Framework Core?

The advice is not to use lazy loading unless you are certain that it is the better solution. This is why (unlike in previous versions of EF) lazy loading is not enabled by default in Entity Framework Core.

What is lazy loading and eager loading in Entity Framework?

Lazy loading in Entity Framework is the default phenomenon that happens for loading and accessing the related entities. However, eager loading is referred to the practice of force-loading all these relations.


2 Answers

Assuming your ".ToList()" issue is while saving back to database, is this what your are looking for?:

 var event = new SS.Entity.Event {Name = "New Name" and other properties};

 IEnumerable<SS.Entity.User> broadcasters = e.Broadcasters
        .Select(u => new SS.Entity.User
             {
              Id = u.Id,
              SkypeId = u.SkypeId,
              Name = u.Name
           });

 var viewers = e.Viewers.Select(u =>
          new SS.Entity.User
          {
             Id = u.Id,
             SkypeId = u.SkypeId,
              Name = u.Name
          });

 //add broadcasters to event
 event.Broadcasters.AddRange(broadcasters);

 //add viewers to event
 event.Viewers.AddRange(viewers);

 dataContext.Events.Add(event);
 dataContext.SaveChanges();
like image 72
Developer Avatar answered Oct 12 '22 21:10

Developer


On your ViewModel, they can just be IEnumerable<T>, unless you need to be doing a .Add() on them later. That can be whatever you want to be. Also, they do not have to be virtual on the Model because you're not having EF create a derived type of your model (again, unless you have other reason to).

To set them back to what they are in the DTO object, if you just want to set it directly and not check individual properties, you can use .ToList() to have it fulfill the ICollection<T> requirement.

// Assuming "var model" is coming in as a parameter
var station = context.Viewers.First();
station.Broadcasters = model.Broadcasters.Select(b => new User {
    Id = b.Id,
    SkypeId = b.SkypeId,
    Name = b.Name
}).ToList();
station.Viewers = model.Viewers.Select(v => new User {
    Id = v.Id,
    SkypeId = v.SkypeId,
    Name = v.Name
});
like image 37
krillgar Avatar answered Oct 12 '22 21:10

krillgar