Search code examples
c#entity-frameworklinqlinq-to-sqllinq-to-entities

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


When using LINQ to Entities, is there a way to reuse a block of a select statment in other select stements?
For example, in the below code, I'm using LINQ to select a customer device object from my database. I also select a related Model object from a table that is joined to the customer devices 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
                        }
                    }
                 })...

There are, however, other objects in the database that also reference the Model table. In my select statements for those other tables, I basically copied the same Model = new Model() code to a select statement for those different tables.
What I'm wondering is, is it possible to store and reuse that block of code in multiple selects? Possibly using an extension method?


Solution

  • As an alternative to Func<>s, you can also make use of extension methods to reuse the conversions between your entity types to and other POCOs.

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

    However, EF will not allow you to nest these, and will complain that it cannot convert the nested extension method to SQL.

    A work around for this can be to perform the transform in memory, rather than in 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()
            });
    }
    

    Since you are now returning an IEnumerable<> any further Where() conditions will be evaluated in memory, rather than in SQL, so its important that ToCustomerDevice() is your last call.