使用“基本”自定义方法将实体模型转换为DTO

问题描述 投票:0回答:2

首先,我道歉,如果这是一个骗局,找到正确的搜索条件似乎是不可能的...

我们正在尝试采用一些最佳实践,并考虑在我们的项目中重构重复代码。在很多情况下,我们有类似的东西;

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 to Entities无法识别方法'Bll.Models.EventModel ConvertPocoToModel(Dal.Pocos.TsrEvent)'方法,并且此方法无法转换为商店表达式。

我知道我们可以添加.ToList()或类似的东西来强制转换发生在C#但我相信这意味着SQL将执行SELECT *而不是SELECT EVentId, EventName, EventCapacity, EventActive

谁能解释一下;

  • 为什么EF在尝试理解如何处理这种简单映射时遇到问题?
  • 为什么间歇性地工作?
  • 我们应该怎么做?
c# entity-framework linq
2个回答
2
投票

实体框架不知道如何翻译您的方法。你必须使用返回qazxsw poi的方法或存储它的属性。

Expression<Func<TsrEvent,EventModel>>

1
投票

你必须要知道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意味着您的流程在本地进行处理。

通过询问Enumerator并重复调用MoveNext来完成最低级别的枚举,直到您不再需要元素为止。像这样:

IEnumerable

您可以显式执行此操作,也可以使用IEnumerable<Student> students = ... IEnumerator<Student> studentEnumerator = students.GetEnumerator(); while (studentEnumerator.MoveNext()) { // there is still a Student to process: Student student = studentEnumerator.Current; ProcessStudent(student); } 或其中一个LINQ函数隐式调用它。

另一方面,foreach意味着由不同的进程处理,通常是数据库管理系统。 IQueryable拥有IQueryableExpressionProvider表示必须以某种通用格式执行的查询。 Expression知道谁必须执行查询(通常是数据库管理系统),以及此进程使用的语言(通常是SQL之类的东西)。

一旦你通过调用GetEnumerator开始枚举,Provider就会被发送到ExpressionProvider试图将Expression翻译成SQL并执行查询。将获取的数据放入可枚举的序列中,并返回枚举数。

回到你的问题

问题是,SQL不知道ConvertPocoToModel。因此,您的提供商无法转换Expression。编译器无法检测到这一点,因为它不知道你的Provider有多聪明。这就是为什么你在调用GetEnumerator之前没有得到这个错误,在你的情况下调用ToList

解决方案是创建一个更改表达式的函数。最简单的方法是扩展功能。见extension methods demystified。这样你可以像任何其他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(),这将浪费处理能力

© www.soinside.com 2019 - 2024. All rights reserved.