首页 > 解决方案 > 我的基础实体未在扩展方法中加载的原因可能是什么?

问题描述

我在使用 Kendo Grid 和 Linq 时遇到困难。我仅在后端使用剑道来简化过滤和排序。一切正常,直到我触及一个具有相关对象的对象。我的对象基础对象未在我的扩展方法中加载,但如果我在 linq select 方法中使用对象初始化它工作正常。也许我的 linq 知识还不够,但我想了解这里发生的窍门是什么?任何帮助将不胜感激。

我的扩展方法给了我 ViewModel :

public static MeetingRoomCultureListViewModel ToMeetingRoomCultureListViewModel(this MeetingRoomCulture meetingRoomCulture) {

        var viewModel = new MeetingRoomCultureListViewModel();

        viewModel.Id = meetingRoomCulture.BaseEntityId;

        viewModel.CancellationDuration = meetingRoomCulture.BaseEntity.CancellationDuration;

        viewModel.MinimumMeetingDuration = meetingRoomCulture.BaseEntity.MinimumMeetingDuration;
        viewModel.OnlyAdminsCanReserve = meetingRoomCulture.BaseEntity.OnlyAdminsCanReserve;
        viewModel.Price = meetingRoomCulture.BaseEntity.Price;
        viewModel.SaltoLockId = meetingRoomCulture.BaseEntity.SaltoLockId;
        viewModel.Status = meetingRoomCulture.Status;
        viewModel.Capacity = meetingRoomCulture.BaseEntity.Capacity;
        viewModel.CleaningDuration = meetingRoomCulture.BaseEntity.CleaningDuration;
        viewModel.Color = meetingRoomCulture.BaseEntity.Color;
        viewModel.Currency = meetingRoomCulture.BaseEntity.Currency;
        viewModel.IsHidden = meetingRoomCulture.BaseEntity.IsHidden;
        viewModel.LocationId = meetingRoomCulture.BaseEntity.LocationId;
        viewModel.LocationName = meetingRoomCulture.BaseEntity.Location.Name;
        viewModel.MaximumDaysForReservationInFuture = meetingRoomCulture.BaseEntity.MaximumDaysForReservationInFuture;
        viewModel.PictureUrl = meetingRoomCulture.BaseEntity.PictureUrl;
        viewModel.Name = meetingRoomCulture.BaseEntity.Name;
        viewModel.TaxRatio = meetingRoomCulture.BaseEntity.TaxRatio;
        viewModel.MaximumMeetingDuration = meetingRoomCulture.BaseEntity.MaximumMeetingDuration;
        viewModel.CreatedDate = meetingRoomCulture.CreatedDate;
        viewModel.LastModifiedDate = meetingRoomCulture.LastModifiedDate;
        return viewModel; 
} 

在这里,这个BaseEntity没有加载,我得到空引用异常。

还有我的api方法:

 public IActionResult Get([FromQuery] [DataSourceRequest] DataSourceRequest request) {
        var ds = _dbContext.MeetingRoomCultures.AsNoTracking().Include(x => x.BaseEntity).ThenInclude(x => x.Location).CultureFilter(CurrentCulture)
            .Select(x => x.ToMeetingRoomCultureListViewModel()).ToDataSourceResult(request);
        return Ok(ds);
    }

但是,如果我使用对象初始化一切正常,并且加载了基本实体,则不会发生异常。如下所示:

 public IActionResult Get([FromQuery] [DataSourceRequest] DataSourceRequest request) {
        var ds = _dbContext.MeetingRoomCultures.AsNoTracking().Include(x => x.BaseEntity).ThenInclude(x => x.Location).CultureFilter(CurrentCulture)
            .Select(x => new MeetingRoomCultureListViewModel()
            {
                Id = x.BaseEntityId,

                CancellationDuration = x.BaseEntity.CancellationDuration,

                MinimumMeetingDuration = x.BaseEntity.MinimumMeetingDuration,
                OnlyAdminsCanReserve = x.BaseEntity.OnlyAdminsCanReserve,
                Price = x.BaseEntity.Price,
                SaltoLockId = x.BaseEntity.SaltoLockId,
                Status = x.Status,
                Capacity = x.BaseEntity.Capacity,
                CleaningDuration = x.BaseEntity.CleaningDuration,
                Color = x.BaseEntity.Color,
                Currency = x.BaseEntity.Currency,
                IsHidden = x.BaseEntity.IsHidden,
                LocationId = x.BaseEntity.LocationId,
                LocationName = x.BaseEntity.Location.Name,
                MaximumDaysForReservationInFuture = x.BaseEntity.MaximumDaysForReservationInFuture,
                PictureUrl = x.BaseEntity.PictureUrl,
                Name = x.BaseEntity.Name,
                TaxRatio = x.BaseEntity.TaxRatio,
                MaximumMeetingDuration = x.BaseEntity.MaximumMeetingDuration,
                CreatedDate = x.CreatedDate,
                LastModifiedDate = x.LastModifiedDate
            }).ToDataSourceResult(request);
        return Ok(ds);
    }

标签: c#entity-frameworklinqkendo-gridentity-framework-core

解决方案


有几个原因。

首先,两个查询都使用投影(Select)到非实体类型,因此属于Ignored 包含类别:

如果您更改查询以使其不再返回查询开始时使用的实体类型的实例,则包含运算符将被忽略。

不同之Select在于评估的位置。自定义(扩展)方法无法转换为 SQL,因此在客户端上执行。由于包含被忽略,您将获得null参考导航属性和null/或空集合导航属性。而在第二种情况下,查询被转换为 SQL 并在服务器(数据库)端执行。SQL 查询中不涉及真正的“对象”或“集合”,只有表和连接。

有关更多信息,请参阅客户端与服务器评估abd查询如何工作(以及基本上与查询数据相关的整个)文档主题。

回顾一下,为了性能(和许多其他原因),总是尝试创建服务器端查询。这意味着根本不使用自定义方法。如果您需要重用逻辑,请将其放入内部Expression<TSource, TResult>,编译委托并从其他地方使用它。

例如:

public static class Selectors
{
    public static readonly Expression<Func<MeetingRoomCulture, MeetingRoomCultureListViewModel>>
    MeetingRoomCultureToListViewModel = source => new MeetingRoomCultureListViewModel
    {
        Id = source.BaseEntityId,

        CancellationDuration = source.BaseEntity.CancellationDuration,

        // the rest ...
    };


    private static readonly Func<MeetingRoomCulture, MeetingRoomCultureListViewModel>
    MeetingRoomCultureToListViewModelFunc = MeetingRoomCultureToListViewModel.Compile();

    public static MeetingRoomCultureListViewModel ToMeetingRoomCultureListViewModel(
        this MeetingRoomCulture source) => MeetingRoomCultureToListViewModelFunc(source);

}

当然也可以使用 LINQ to Entities 查询中的表达式:

var ds = _dbContext.MeetingRoomCultures // no tracking, no includes
    .CultureFilter(CurrentCulture)
    .Select(Selectors.MeetingRoomCultureToListViewModel) // <--
    .ToDataSourceResult(request);

正如评论中提到的,像AutoMapper这样的 3rd 方库可以大大简化从/到实体模型的转换。


推荐阅读