Reputation: 26362
Look at the following senario:
class CompanyDto {
public string Street { get; set;}
public int Number { get; set;}
public bool IsAttr1 { get; set;}
public bool IsAttr2 { get; set;}
public bool IsAttr3 { get; set;}
// Other not common Properties
}
class AnimalDto {
public string Street { get; set;}
public int Number { get; set;}
// Other not common Properties
}
class HouseDto {
public bool IsAttr1 { get; set;}
public bool IsAttr2 { get; set;}
public bool IsAttr3 { get; set;}
// Other not common Properties
}
class PersonDto {
public string Street { get; set;}
public int Number { get; set;}
public bool IsAttr1 { get; set;}
public bool IsAttr2 { get; set;}
public bool IsAttr3 { get; set;}
// Other not common Properties
}
I have these Dtos that I want to populate from Entinty Framework context. Thing is I want to filter the results using standard where clauses. Example:
List<PersonDto> prs = context.Person.Where(x => x.Number == SomeValue && Street == SomeOtherValue).Where( x=> x.IsAttr1 || x.IsAttr2).Select(//projection here).ToList();
List<AnimalDto> anmls = context.Animal.Where(x => x.Number == SomeValue && Street == SomeOtherValue).Select(//projection here).ToList();
etc.
I want to project them to the Dtos (simple) but I don't want to write the Where clauses again and again. I tried to do this using extention Methods but failed misserably both while using base classes (Address and Flags) and while using Interfaces because IQuerable cannot be cast from Interface to Concrete class.
Is there any way I can do this?
The preferable way would be to have an extention method like this:
public static IQueryable<IAddress> WhereAddress(this IQueryable<IAddress> qry, int SomeValue, int SomeOtherValue)
{
return qry.Where(x => x.Number == SomeValue && Street == SomeOtherValue);
}
But I cannot add the WhereFlags after the WhereAddress clause and the IQuerable.cast(); fails to do the cast.
Upvotes: 1
Views: 76
Reputation: 35544
You could implement your extension method as follows with a constraint on the generic parameter to be of type IAddress
and class
(this avoids the exception "LINQ to Entities only supports casting EDM primitive or enumeration types") and implement the interface on your types Company
, Animal
and Person
public static IQueryable<TAddress> WhereAddress<TAddress>(this IQueryable<TAddress> qry, int SomeValue, string SomeOtherValue)
where TAddress : class, IAddress {
return qry.Where(x => x.Number == SomeValue && x.Street == SomeOtherValue);
}
With this you can call
context.Person.WhereAddress(SomeValue,SomeOtherValue).Where(x=> x.IsAttr1 || x.IsAttr2);
but not
context.House.WhereAddress(SomeValue,SomeOtherValue);
cause House
does not implement IAddress
.
Upvotes: 3
Reputation: 82096
How about something like:
public IQueryable<T> GetByAddress<T>(DbContext context, int number, string street)
{
return context.Set<T>().Select<T, IAddress>(x => x.Number == SomeValue && x.Street == SomeOtherValue);
}
Then externally you should be able to do
List<PersonDto> prs = GetByAddress<Person>(context, 1, "1234 Street")
.Select(...)
.ToList();
List<AnimalDto> anmls = GetByAddress<Animal>(context, 2, "5678 Road")
.Select(...)
.ToList();
If you prefer the extension method approach then you should be able to do that, the reason it doesn't work at the minute is because you aren't projecting the table to IAddress
. LINQ doesn't support implicit casting e.g.
List<PersonDto> prs = context.Person.Select<Person, IAddress>()
.WhereAddress(1, "1234 Street")
.Select(...)
.ToList();
List<PersonDto> prs = context.Animal.Select<Animal, IAddress>()
.WhereAddress(2, "5678 Street")
.Select(...)
.ToList();
Upvotes: 1