是否可以重用 LINQ to Entities select 语句的部分内容?

Is it possible to reuse parts of a LINQ to Entities select statement?

使用 LINQ to Entities 时,有没有办法在其他 select 语句中重用 select 语句块?
例如,在下面的代码中,我使用 LINQ 从我的数据库中 select 一个客户设备对象。我还 select 来自 table 的相关模型对象连接到客户设备 table。

            list = context.PTT_CUSTOMER_DEVICES
                .Include(...)
                .Select(d => new CustomerDevice
                {
                    customerAssetTag = d.CustomerAssetTag,
                    customerDeviceID = d.CustomerDeviceID,
                    //This section is used in several LINQ statements throughout the application.
                    Model = new Model()
                    {
                        ModelID = d.PTS_MODELS.ModelID,
                        Name = d.PTS_MODELS.Name,
                        Make = new Make()
                        {
                            MakeID = d.PTS_MODELS.PTS_MAKES.MakeID,
                            Name = d.PTS_MODELS.PTS_MAKES.Name
                        }
                    }
                 })...

但是,数据库中还有其他对象也引用模型 table。在我针对其他 table 的 select 语句中,我基本上将相同的 Model = new Model() 代码复制到针对那些不同 table 的 select 语句中。
我想知道的是,是否可以在多个 select 中存储和重用该代码块?可能使用扩展方法?

如果这些表具有共同的基础 class 或实现相同的接口,您可以这样做。

为简单起见,假设我有一个 IQueryable<string> names。我可以这样排序:

IQueryable<string> sorted = names.OrderBy(name => name);

但是如果我想保留这种特定类型以备后用,我可以这样做:

Func<IQueryable<string>, IQueryable<string>> orderBy = q => q.OrderBy(x => x);

现在调用它,我只是将任何 IQueryable<string> 传递给:

IQueryable<string> sorted = orderBy(names);

您需要为 Model 创建一个接受 CustomerDevice 的构造函数吗? (实体是什么 PTT_CUSTOMER_DEVICES:

public Model(CustomerDevice d) {
    ModelID = d.PTS_MODELS.ModelID;
    Name = d.PTS_MODELS.Name;
    Make = new Make() {
        MakeID = d.PTS_MODELS.PTS_MAKES.MakeID,
        Name = d.PTS_MODELS.PTS_MAKES.Name
    };
}

然后您可以在 LINQ 中调用该构造函数:

Model = new Model(d),

作为 Func<> 的替代方法,您还可以使用扩展方法来重用您的实体类型与其他 POCO 之间的转换。

public static IQueryable<CustomerDevice> ToCustomerDevice(this IQueryable<PTT_CUSTOMER_DEVICES> devices)
{
    return devices.Select(d => new CustomerDevice
    {
        customerAssetTag = d.CustomerAssetTag,
        customerDeviceID = d.CustomerDeviceID
    }
}

但是,EF 不允许您嵌套这些,并且会抱怨它无法将嵌套的扩展方法转换为 SQL。

解决这个问题的方法是在内存中执行转换,而不是在 SQL:

public static Model ToModel(this PTS_MODELS model)
{
    return new Model()
    {
        ModelID = model.ModelID,
        Name = model.Name,
        Make = new Make()
        {
            MakeID = model.PTS_MAKES.MakeID,
            Name = model.PTS_MAKES.Name
        }
    };
}

public static IEnumerable<CustomerDevice> ToCustomerDevice(this IQueryable<PTT_CUSTOMER_DEVICES> devices)
{
    return devices
        .Include(d => d.PTS_MODELS.PTS_MAKES)
        .AsEnumerable() // Evaulate everything that follows in memory
        .Select(d => new CustomerDevice
        {
            customerAssetTag = d.CustomerAssetTag,
            customerDeviceID = d.CustomerDeviceID,
            Model = d.PTS_MODELS.ToModel()
        });
}

由于您现在要返回一个 IEnumerable<>,任何进一步的 Where() 条件都将在内存中进行评估,而不是在 SQL 中进行评估,因此 ToCustomerDevice() 很重要最后通话。

您可以使用表达式:

public static Expression<Func<PTT_CUSTOMER_DEVICES, CustomerDevice>>
            CustomerDeviceExpression = d =>
                new CustomerDevice
                {
                    customerAssetTag = d.CustomerAssetTag,
                    customerDeviceID = d.CustomerDeviceID,

                    Model = new Model()
                    {
                        ModelID = d.PTS_MODELS.ModelID,
                        Name = d.PTS_MODELS.Name,
                        Make = new Make()
                        {
                            MakeID = d.PTS_MODELS.PTS_MAKES.MakeID,
                            Name = d.PTS_MODELS.PTS_MAKES.Name
                        }
                    }
                 };

然后:

list = context.PTT_CUSTOMER_DEVICES
            .Include(...)
            .Select(CustomerDeviceExpression)...