I have encountered a LINQ issue and hope that you can help me to figure it out.
Here is what is happening.
- I get an
IQueryable<LicenseEntity>
of entities from the repository. - I look at the fields in these entities and see that they contain valid data. There is a field for a related entity
Customer
in the LicenseEntity. It contains valid data, too, because I loaded it with the LicenseEntity. - I use
.Select
to project each LicenseEntity to a LicenseViewModel. - For each LicenseEntity, a LicenseEntity is passed into
AutoMapper.Mapper.Map
and is loaded into a LicenceViewModel entity. - After all of the entities have been processed, when I look at the list of LicenseViewModels in the debugger, it reports a null reference exception and there are no items to view.
- To determine whether AutoMapper what causing my problem, I replaced it with a
MapMe()
. When I stopped at the return statement in MapMe and looked at thes
parameter, which is the original entity, I found that the data in it is okay except that the customer field is now null. I assume thatSelect
has done something that I don't know about.
How I can make Select
retain all of the information in the original entity when it is doing its projection? Our solution cannot materialize the list because it may be very, very large. I've included my test code below and would really appreciate your help.
// Get the IQueryable<LicenseEntity> list of licenses from the repository.
var list = LicenseRepository.List();
// Convert the IQueryable<LicenseEntity> to an IQueryable<LicenseViewModel>
var vmlist = list.Select(x => MapMe(x, new LicenseViewModel()));
//var vmlist = list.Select(x => AutoMapper.Mapper.Map(x, new LicenseViewModel()));
// This function was used to see the LicenseEntity that was passing into Map().
// I discovered that the entity has all the correct data except for a related
// entity field, which was present in the original LicenseEntity before
public LicenseViewModel MapMe(LicenseEntity s, LicenseViewModel d)
{
return d;
}
The following code works properly however it materializes the entities, which we cannot do.
List<LicenseViewModel> vms = new List<LicenseViewModel>();
foreach (var item in list)
{
var vm = AutoMapper.Mapper.Map(item, new LicenseViewModel());
vms.Add(vm);
}
You've tagged this
LINQ-to-Entities
but there's no mention of the underlying technology in the text. But it's very likely that the problem is caused by lazy loading of associated objects.This is a design choice that applies to most ORMs that I've worked with. When you load an object, connected objects are not loaded by default. If they were loaded by default it's quite clear you'd quickly break everything
The answer is that you need to specify which related objects to load. In the Entity Framework you do this using the
Include
method.Because you are using a repository abstraction you might find this more difficult than it needs to be, but without knowing more I can't give any advice. This type of functionality - pretty basic stuff - is always a difficulty with repositories and 'unit-of-work' patterns.
I've found the solution for projecting domain entities to viewmodels. If you are struggling with the same kind of issue as I had, please review the following links:
http://lostechies.com/jimmybogard/2011/02/09/autoprojecting-linq-queries/ http://www.devtrends.co.uk/blog/stop-using-automapper-in-your-data-access-code
By the way, in one one my domain entities, I had a partial class with some "calculated" properties... Properties whose values were generated from other fields in the database record. These cannot be in the domain entity because they will interfere with the aforementioned solutions. I moved them to my ViewModel class, which is where they were really required, and all is well.
Hope that this info helps...
Mike
I think your mapping should be more like:
(ie - you don't need the
foreach
loop).But unfortunately I doubt very much that that'll fix your issue as I suspect that'll also materialize your entities.