【问题标题】:Is it possible to reuse parts of a LINQ to Entities select statement?是否可以重用部分 LINQ to Entities 选择语句?
【发布时间】:2017-11-08 21:40:21
【问题描述】:

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

            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
                        }
                    }
                 })...

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

【问题讨论】:

  • 您将 lambda 传递给该 Select,您可以将其存储在变量中并重用。您还可以将模型部分保存到函数中并将所需的所有内容作为参数传递给它,您还可以通过 Automapper 或类似工具在变量 dCustomerDeviceModel 之间创建映射,并使用映射那个。

标签: c# entity-framework linq linq-to-sql linq-to-entities


【解决方案1】:

作为Func<>s 的替代方案,您还可以使用扩展方法来重用实体类型与其他 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&lt;&gt;,任何进一步的Where() 条件都将在内存中而不是在SQL 中进行评估,因此ToCustomerDevice() 是您最后一次调用很重要。

【讨论】:

  • 效果很好!我从来没有想过要像那样在内存中进行转换。你让我的生活轻松了一千倍。
【解决方案2】:

如果这些表有一个共同的基类或实现相同的接口,你可以这样做。

为了简单起见,假设我有一个IQueryable&lt;string&gt; names。我可以这样排序:

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

但如果我想保留这种特殊类型以供以后使用,我可以这样做:

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

现在调用它,我只需将任何 IQueryable&lt;string&gt; 传递到:

IQueryable<string> sorted = orderBy(names);

【讨论】:

    【解决方案3】:

    您需要为 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),
    

    【讨论】:

    • 感谢您的反馈,但不幸的是这不起作用,因为 LINQ to entity 只接受无参数构造函数。
    • 对不起,我没想到。我认为你应该研究一下 LINQKit。
    【解决方案4】:

    你可以使用表达式:

    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)...
    

    【讨论】:

    • 您好,欢迎来到 stackoverflow,感谢您的回答。虽然这段代码可能会回答这个问题,但您是否可以考虑添加一些解释来说明您解决了什么问题,以及您是如何解决的?这将有助于未来的读者更好地理解您的答案并从中学习。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-20
    相关资源
    最近更新 更多