我的基本实体未加载到扩展方法中是什么原因?

时间:2018-09-12 07:34:33

标签: c# entity-framework linq kendo-grid entity-framework-core

我在使用Kendo Grid和Linq时遇到困难。我仅在后端使用kendo来简化过滤和排序。一切正常,直到我触摸了具有相关对象的对象。我的对象基础对象未加载到扩展中方法,但是如果我在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);
    }

1 个答案:

答案 0 :(得分:3)

有几个原因。

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

  

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

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

有关更多信息,请参见Client vs. Server EvaluationHow Queries Work(基本上与整个Querying Data相关)文档主题。

回顾一下,出于性能(以及许多其他原因)的原因,请始终尝试创建服务器端查询。这意味着根本不使用自定义方法。如果您需要重用逻辑,请将其放入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之类的第三方库可以大大简化从/到实体模型的转换。