首先,如果这是骗子,我深表歉意,发现正确的搜索词似乎是不可能的...
我们正在尝试采用一些最佳实践,并研究项目中重复的重复代码。在许多情况下,我们有类似的东西
public List<EventModel> GetEvents(bool showInactive, bool showPastEvents)
{
return eventRepository
.GetEvents(_customerId, showInactive, showPastEvents)
.Select(e => New EventModel() { Id = e.EventId, Name = e.EventName, Capacity = e.EventCapacity, Active = e.EventActive })
.ToList();
}
所以我们尝试做这样的事情;
public List<EventModel> GetEvents(bool showInactive, bool showPastEvents)
{
return eventRepository
.GetEvents(_customerId, showInactive, showPastEvents)
.Select(e => ConvertPocoToModel(e))
.ToList();
}
private EventModel ConvertPocoToModel(TsrEvent tsrEvent)
{
EventModel eventModel = new EventModel()
{
Id = tsrEvent.EventId,
Name = tsrEvent.EventName,
Capacity = tsrEvent.EventCapacity,
Active = tsrEvent.EventActive
};
return eventModel;
}
有时这有效,但是间歇性地;
system.notsupportedException:'linq到实体不认识 方法'bll.models.eventmodel convertpocotomodel(dal.pocos.tsrevent)' 方法,并且该方法不能翻译成商店表达式。'
我知道我们可以添加.tolist()或类似地迫使转换发生在C#中,但我相信这意味着SQL将执行SELECT *
而不是SELECT EVentId, EventName, EventCapacity, EventActive
任何人都可以解释;
- 为什么EF遇到问题,试图了解如何处理这个简单的映射?
- 为什么它间歇性工作?
- 我们应该怎么做?
实体框架不知道如何翻译您的方法。您必须使用返回Expression<Func<TsrEvent,EventModel>>
或存储它的属性的方法。
public List<EventModel> GetEvents(bool showInactive, bool showPastEvents)
{
return eventRepository
.GetEvents(_customerId, showInactive, showPastEvents)
.Select(ConvertPocoToModelExpr)
.ToList();
}
private static Expression<Func<TsrEvent,EventModel>> ConvertPocoToModelExpr => (x)=>new EventModel()
{
Id = x.EventId,
Name = x.EventName,
Capacity = x.EventCapacity,
Active = x.EventActive
};
您必须意识到IEnumerable
和IQueryable
之间的差异。
一个IEnumerable
对象将所有内容都列举在序列上。您可以要求第一个元素,只要有下一个元素,就可以要求下一个元素。IEnumerable
的目的是通过您的过程在本地进行。
最低级别的枚举是通过要求枚举者并反复调用moveNext完成的,直到您不再需要元素为止。这样:
IEnumerable<Student> students = ...
IEnumerator<Student> studentEnumerator = students.GetEnumerator();
while (studentEnumerator.MoveNext())
{
// there is still a Student to process:
Student student = studentEnumerator.Current;
ProcessStudent(student);
}
您可以明确执行此操作,或使用foreach
或LINQ函数之一隐式称其为
另一方面,IQueryable
通常是通过不同的过程(通常是数据库管理系统)处理的。IQueryable
容纳Expression
和Provider
。Expression
表示必须以某种通用格式执行的查询。Provider
知道谁必须执行查询(通常是数据库管理系统),以及此过程使用的语言(通常是SQL喜欢)。
通过调用getEnumerator开始枚举,Expression
就会发送到Provider
,后者试图将Expression
转换为SQL并执行查询。获取的数据放入枚举序列中,并返回枚举器。
回到您的问题
问题是,SQL不知道ConvertPocoToModel
。因此,您的提供商无法转换Expression
。编译器无法检测到这一点,因为它不知道您的Provider
有多聪明。这就是为什么您在调用GetEnumerator
之前不会遇到此错误的原因,在您的情况下,请致电ToList
。
解决方案
解决方案是制定一个更改表达式的函数。最简单的方法是扩展功能。请参阅扩展方法神秘。这样,您可以像其他任何LINQ方法一样使用它:
public static IQueryable<EventModel> ToEventModels(this IQueryable<TsrEvent> tsrEvents)
{
return tsrEvent.Select(tsrEvent => new EventModel
{
Id = tsrEvent.EventId,
Name = tsrEvent.EventName,
Capacity = tsrEvent.EventCapacity,
Active = tsrEvent.EventActive
};
}
请注意,我省略了构造函数中的()
:SQL无法调用构造函数!
用法:
var result = dbContext.TsrEvents
.Where(tsrEvent => tsrEvent.Active && tsrEvent.Date == Today)
.ToEventModels()
.GroupBy(...)
... etc
或,如果您的GetEvents
返回IQueryable<TsrEvents>
return eventRepository.GetEvents(_customerId, showInactive, showPastEvents)
.ToEventModels();
最终备注
最好让您的数据提取功能尽可能长时间返回IQueryable<...>
和IEnumerable<...>
。只让最终用户实现查询。如果您执行ToList()
,并且您的呼叫者只想做FirstOrDefault()