c# - 我的基础实体未在扩展方法中加载的原因可能是什么?
问题描述
我在使用 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);
}
解决方案
有几个原因。
首先,两个查询都使用投影(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 方库可以大大简化从/到实体模型的转换。
推荐阅读
- google-bigquery - 跨各种 BigQuery 表分布数据
- php - 如何在运行 PHP 的 Docker 容器中自动启动 cron?
- sql-server-2014 - 适用于 SQL Server 2014 或更早版本的时态表功能
- reactjs - Redux 减速器状态在安装组件时返回 NaN 值,但在更新时工作正常
- networking - 在动态 IP 上托管我的世界服务器的安全方式
- python - 如何在 pandas.Timestamp.today() 函数中只保留日期 YYYY-MM-DD?
- c++ - 如何从 Windows 为 Linux 编译 C++ 代码(Dev-Cpp)
- c# - 如何判断 Skia 阴影所需的空间量
- bigcommerce - 在 Windows 10 上运行 Big Commerce 的 Stencil CLI
- java - 如何创建适当的 POJO 来提供 JSON 响应?