使用带有 AsEnumerable 的 EF 的构造函数投影到域 class

Projecting to domain class with constructor with EF with AsEnumerable

TL;DR

AsEnumerable()IQueryable() 上使用是否更安全(例如,它是否已经作为 ToList() 执行)以获得错误 [=17 的解决方法(见下文) =]?

换句话说,IQueryable() 上使用 AsEnumerable() 有什么影响,尤其是在将 Where() 链接到它时。

请阅读下面的完整上下文和信息

长版

我正在为我的存储库层实现一个抽象,因为我必须能够从 JSON 文件、XML 文件以及 EntityFramework(数据库)中读取数据。

问题描述

我在投影我的 EF 实体时遇到错误 automapper Only parameterless constructors and initializers are supported in LINQ to Entities. 执行如下代码时:

public IEnumerable<Person> All() {
    return _dataContext
        .People
        .Select(p => new Person(p.Id, p.FirstName, p.LastName));                
}

作为参考,这是我的DbContext,所以你看到上面的_dataContext.Peoplereturns一个IQueryable<EFPerson>:

public class EFDataContext : DbContext
{       
    public IDbSet<EFPerson> People { get; set; }

    public EFDataContext() 
        : this(Settings.Default.EFDataContextConnectionString) { }
    public EFDataContext(string nameOrConnectionString)
        : this(() => nameOrConnectionString) { }
    public EFDataContext(Func<string> connectionStringProvider) 
        : base(connectionStringProvider()) { }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        modelBuilder.Entity<EFPerson>()
            .HasKey(p => p.Id)
            .ToTable(Settings.Default.PeopleTable);
    }
}

我的解决方案

我不想使用 AutoMapper,我也不想让我的域实体具有 setters - 因为它们需要 immutable/read-only 用于业务模型我在写。

我想出的解决方案是使用 .AsEnumerable(),然后使用我的域实体的构造函数进行投影:

public IEnumerable<Person> All() {
    return _dataContext
        .People
        .AsEnumerable()
        .Select(p => new Person(p.Id, p.FirstName, p.LastName));                
}

代码运行速度很快,之后我还可以在域实体上进行 .Where 投影。 我认为这是安全的,因为我的理解是 .AsEnumerable 不会像 .ToList 那样立即评估。

问题回顾

我的问题 这样,我的假设是否正确。这样做是安全的解决方法,还是我应该以不同的方式建模 - 使用 AutoMapper 或在我的 EntityFramework 服务层/存储库实现中编写更长的逻辑?

由于你的问题很广泛,我将描述我的解决方案: 在 Entity Framework:

中使用您的域实体
public class EFDataContext : DbContext
{       
    public IDbSet<Person> People { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        modelBuilder.Entity<Person>()
            .HasKey(p => p.Id)
            .ToTable(Settings.Default.PeopleTable);
    }
}

根据需要设计您的实体:

public class Person
{
    private Person() //for EF
    {
    }
    public Person(string name) //for me
    {
        Name = name;
    }
    public int Id { get; private set; }
    public string Name { get; private set; }
    public string LastName { get; private set; }
}

查询:

public IEnumerable<Person> All() {
    return _dataContext
        .People
        .AsEnumerable();                
}

为什么我在这里使用AsEnumerable?只是为了隐藏我的数据库,它是 IQueryable。 如您所见,EF 允许使用域对象。