Linq Projection is not including all data from the

2019-07-21 14:52发布

I have encountered a LINQ issue and hope that you can help me to figure it out.

Here is what is happening.

  1. I get an IQueryable<LicenseEntity> of entities from the repository.
  2. 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.
  3. I use .Select to project each LicenseEntity to a LicenseViewModel.
  4. For each LicenseEntity, a LicenseEntity is passed into AutoMapper.Mapper.Map and is loaded into a LicenceViewModel entity.
  5. 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.
  6. 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 the s 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 that Select 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);
}

3条回答
别忘想泡老子
2楼-- · 2019-07-21 15:14

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

  • when you load a Licence, the related Customer is automatically loaded
  • when the Customer is loaded all related objects are loaded - Company, Address, all other Licences, etc
  • for each of those objects, every related object is loaded...

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.

查看更多
beautiful°
3楼-- · 2019-07-21 15:15

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

查看更多
祖国的老花朵
4楼-- · 2019-07-21 15:24

I think your mapping should be more like:

var vms = Mapper.Map<List<LicenseEntity>, List<LicenseViewModel>>(list);

(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.

查看更多
登录 后发表回答