我在使用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);
}
答案 0 :(得分:3)
有几个原因。
首先,两个查询都使用对非实体类型的投影(Select
),因此属于Ignored includes类别:
如果更改查询以使其不再返回查询开始的实体类型的实例,则将忽略include运算符。
差异是其中 Select
的值。自定义(扩展)方法无法转换为SQL,因此在客户端上执行。由于将忽略包含,因此您将获得null
参考导航属性和null
或空集合导航属性。在第二种情况下,查询被转换为SQL并在服务器(数据库)端执行。 SQL查询中没有真正的“对象”或“集合”,只是表和联接。
有关更多信息,请参见Client vs. Server Evaluation和How 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之类的第三方库可以大大简化从/到实体模型的转换。