Reputation: 35
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?
Upvotes: 1
Views: 1312
Reputation: 29
You can use Expression:
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
}
}
};
Then:
list = context.PTT_CUSTOMER_DEVICES
.Include(...)
.Select(CustomerDeviceExpression)...
Upvotes: 0
Reputation: 26917
You would need to create a constructor for Model
that takes a CustomerDevice
? (what ever the entity is for 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
};
}
Then you could just call that constructor in the LINQ:
Model = new Model(d),
Upvotes: 0
Reputation: 36
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.
Upvotes: 1
Reputation: 117084
If those tables have a common base class or implement the same interface you can do it.
To make it simple assume I have a IQueryable<string> names
. I can sort it like this:
IQueryable<string> sorted = names.OrderBy(name => name);
But if I want to keep this particular sort to use later I can do this:
Func<IQueryable<string>, IQueryable<string>> orderBy = q => q.OrderBy(x => x);
Now to call it I just pass any IQueryable<string>
in:
IQueryable<string> sorted = orderBy(names);
Upvotes: 0