I have Property and PropertyCompliance entities that look something like this...
public class Property{
public virtual ICollection<PropertyCompliance> ComplianceRecords {get;set;}
}
public class PropertyCompliance{
public virtual Property {get;set;}
public DateTime ComplianceDate {get;set;}
public ComplianceRating ComplianceValue {get;set;} //just an enum
}
In a number of places I need to find the PropertyCompliance row closest to a particular date.
var complianceRating = property.ComplianceRecords.OrderBy(cr=>cr.Date)
.Where(cr=>cr.ComplianceDate< checkDate).FirstOrDefault();
I know I could use an expression as such to filter the ComplianceRecords:
var complianceRating = property.ComplianceRecords.Where(SomeExpression)
.OrderBy(cr=>cr.ComplianceDate).FirstOrDefault()
However this isn't really reducing the amount of repetition as all it's doing is replacing the Where() statement.
Is there a way to apply the expression to the Property to allow this filtering to occur within another expression? Something like:
private static Expression<Func<Property, ComplianceRating>> PropertyComplianceForDate(DateTime checkDate)
{
return p => p.ComplianceRatings
.OrderByDescending(cr => cr.Date)
.First(cr => cr.Date <= checkDate).ComplianceRating;
}
public Expression<Func<Property, bool>> PropertyIsCompliant(DateTime checkDate)
{
return (p) => PropertyComplianceForDate(checkDate) == ComplianceRating.Compliant;
}
With "PropertyComplianceForDate" being an expression that could be translated to SQL to allow the PropertyIsCompliant expression to be used in SQL also.
You could declare an expression at class level and reuse it.
Alternatively you could make a method on your table or an extension method. Extension method (inside a static class) would look something like:
public static PropertyCompliance PropertyIsCompliant(this IEnumerable<PropertyCompliance> complianceRecords, DateTime checkDate) {
return complianceRatings
.OrderByDescending(cr => cr.Date)
.First(cr => cr.Date <= checkDate);
}
Related
Let's say I have a simple model:
public class Movie
{
public int ID { get; set; }
public string Name { get; set; }
}
And a DbContext:
public class MoviesContext : DbContext
{
...
public DbSet<Movie> Movies { get; set; }
}
Also I have a method in MoviesContext class that filters Movies by substring like this:
return Movies.Where(m => m.Name.Contains(filterString)).Select(m => m);
Now suppose I'd like to add a new model, say:
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string FullName { get { return FirstName + (MiddleName?.Length > 0 ? $" {MiddleName}" : "") + $" {LastName}"; } }
}
I also want to filter persons (DbSet Persons) by name (i.e. FullName). I'd like DRY, so it is preferrable to generalize a filter method of MoviesContext. And, what is important, I'd like to do filtering on the database level. So I have to deal with LINQ for Entities.
If not for this, the task is pretty simple. I could use an abstract class and add a virtual method that do the "contains substring" logic. Alternatively, I could use an interface. Unfortunately, because of LINQ for Entities, I can't use a FullName property (which is not convenient but bearable) and I can't write something like this:
return dbset.Where(ent => ent.NameContains(filterString)).Select(ent => ent);
So, how to solve this problem? I've found some solution (almost have my head broken), but I am not very happy with it. I'll post my solution separately, but I hope there is a more elegant one.
Reading your code a little more closely, instead of your NameFilterable abstract class, could you not do something like this:
public interface IHasPredicateGetter<T> {
[NotNull] Expression<Func<T, bool>> GetPredicateFromString([NotNull] string pValue);
}
public class Movie : IHasPredicateGetter<Movie> {
public int ID { get; set; }
public string Name { get; set; }
public Expression<Func<Movie, bool>> GetPredicateFromString(string pValue) {
return m => m.Name.Contains(pValue);
}
}
This prevents you from needing a cast, for example. It's so hard to grasp just what you're trying to do here, so I'm not sure this is the whole thing or not. You're still stuck with an instance method that should probably be a static method, but it couldn't implement an interface otherwise.
My solution looks like this.
[1] The base class:
public abstract class NameFilterable
{
protected static Expression<Func<T, bool>> False<T>() { return f => false; }
public virtual Expression<Func<T, bool>> GetNameContainsPredicate<T>(string filterString)
{
return False<T>();
}
}
[2] The Person class (I'll omit the Movie class, it is more simple):
public class Person : NameFilterable
{
...
public override Expression<Func<T, bool>> GetNameContainsPredicate<T>(string filterString)
{
return entity =>
String.IsNullOrEmpty(filterString) ||
(entity as Person).LastName.Contains(filterString) ||
(entity as Person).FirstName.Contains(filterString) ||
(((entity as Person).MiddleName != null) && (entity as Person).MiddleName.Contains(filterString))
;
}
}
[3] The filter methods in MoviesContext:
private static IQueryable<T> _filterDbSet<T>(DbSet<T> set, Expression<Func<T, bool>> filterPredicate) where T : class
{
return set
.Where(filterPredicate)
.Select(ent => ent);
}
private static IQueryable<T> _filterDbSet<T>(DbSet<T> set, string search = null) where T : NameFilterable, new()
{
T ent = new T();
return _filterDbSet<T>(set, (ent as NameFilterable).GetNameContainsPredicate<T>(search));
}
public static ICollection<T> Filter<T>(DbSet<T> set, string search = null) where T : NameFilterable, new()
{
return _filterDbSet(set, search).ToList();
}
And it seems that all this works pretty well. But I can't say it is very elegant.
[1] I have to use a generic T, though on the Person level I always work with Person objects (or descendants). So I have to convert T to Person (as Person).
[2] In GetNameContainsPredicate method, I can't write (because of LINQ for Entities):
return entity =>
{
Person p = entity as Person;
String.IsNullOrEmpty(filterString) ||
p.LastName.Contains(filterString) ||
p.FirstName.Contains(filterString) ||
((p.MiddleName != null) && p.MiddleName.Contains(filterString))
};
[3] I can't use static methods (statics couldn't be overridden), so I have to create a dummy T object (T ent = new T();).
[4] I still can't use a FullName.Contains(filterString)
So, the question remains: Maybe I miss something and there is a more elegant solution to the problem?
You could create a method that is responsible for search if a type has a particular property and filter for that property,if the object has not the property simply return null. Whith this you can create an expression that filters for this property
//gets the property info of the property with the giving name
public static PropertyInfo GetPropetyInfo<T>(string name)
{
var type = typeof(T);
var property = type.GetProperty(name);
return property;
}
//Creates an expression thats represents the query
public static Func<T, bool> GetFilterExpression<T>( string propertyName, object propertyValue)
{
var prop = GetPropetyInfo<T>(propertyName);
if(prop==null)return t=>false;
var parameter = Expression.Parameter(typeof(T), "t");
Expression expression = parameter;
var left = Expression.Property(expression, prop);
if (prop.PropertyType == typeof(string))
{
var toLower = typeof(string).GetMethods().FirstOrDefault(t => t.Name.Equals("ToLower"));
var tlCall = Expression.Call(left, toLower);
var right = Expression.Constant(propertyValue.ToString().ToLower());
var contains = Expression.Call(tlCall, typeof(string).GetMethod("Contains"), right);
var containsCall = Expression.IsTrue(contains);
expression = Expression.AndAlso(Expression.NotEqual(left, Expression.Constant(null)), containsCall);
}
else
{
if (prop.PropertyType.ToString().ToLower().Contains("nullable"))
{
var getValue = prop.PropertyType.GetMethods().FirstOrDefault(t => t.Name.Equals("GetValueOrDefault"));
var getValueCall = Expression.Call(left, getValue);
var right = Expression.Constant(propertyValue);
expression = Expression.Equal(getValueCall, right);
}
else
{
var value = Convert.ChangeType(propertyValue,prop.PropertyType);
var right = Expression.Constant(value);
expression = Expression.Equal(left, right);
}
}
return Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { parameter }).Compile();
}
The you can use it as follow
var expression = YOURCLASS.GetFilterExpression<Person>("LastName", "Jhon");
var result=dbset.Where(expression);
There are a few things I've done to get polymorphism with EF, but in your specific case of wanting reusable filters, I'm not sure it's worth the trouble. I've basically tried to do the same exact thing, but every time I end up realizing that there's no point. Ask yourself: what exactly are the benefits of doing this, and how is it any more flexible than what a Where clause already offers?
There are two issues. One is that it's hard or nigh impossible to get a filter to be used between two separate classes by using a shared interface (INamedObject for example). This is because you need a strongly typed expression. You can make a function that returns a strongly typed expression, but why would you not have just wrote the expression in the first place? The other issue is that you need a new filter expression for every search value, which is pretty close to where we are already.
If you perfected this, what would you have? The ability to infer type, specify a search value, and get an expression you could use? Isn't that essentially what we already have? The way Where clauses already are, they already have strong typing, and the ability to use dynamic search values. While it might feel a tiny bit redundant to say x => x.Name == value in more than one place, really the ability to specify such a concise and powerful filter statement is already a pretty amazing place to be.
Scenario:
I Have a table FooTable when column Foo is varchar(8) NOT NULL and the info in this column is like:
Foo
-----------
13940-00
13940-01
13940-02
13941-00
13941-01
Where the numeric part after the hyphen (-) always have two digits.
Problem:
I'm using Ado.net Entity Framework, and I created a class with 2 static methods to help get first and second part of the number:
public class HelperFoo
{
public static string Prefix(string value) { /* code here */ }
public static string Suffix(string value) { /* code here */ }
}
So now I can do something like this:
context.FooTable.Where(w => HelperFoo.Prefix(w.Foo) == "13940");
But, as you probably already know, this command line throws a NotSupportedException. It's because LINQ don't recognize HelperFoo.Prefix, so it can't convert the expression in SQL.
I can write a block of code in SQL that do the same that my methods of HelperFoo so I can create the SQL to my methos.
Question
Can I create something (class, method, or other) that makes LINQ knows my method when I executed the LINQ code?
EDITED
PS: I need something generic that works like a method or SQL function because I need to get this Prefix and Suffix in scenarios like Select, OrderBy, GroupBy and many others.
You could try creating your own IQueryable filters for the FooTable, a bit like this:
public static class Filters
{
public static IQueryable<FooTable> WithPrefix(this IQueryable<FooTable> item, string prefix)
{
return item.Where(i => i.Foo.StartsWith(prefix));
// note that this should be the same code you have in the
// Prefix() method inside HelperFoo...
}
}
Which you can use like this:
context.FooTable.WithPrefix("13940");
UPDATE: Sadly the second option here does not work:
Another option would be to have the helper methods not return a value but a Predicate<> for FooTable:
public class HelperFoo
{
public static Func<FooTable, bool> Prefix(string value)
{
return (i) => i.Foo.Substring(0, 5) == value;
}
public static Func<FooTable, bool> Suffix(string value) { /* code here */ }
}
And use it like this:
context.FooTable.Where(HelperFoo.Prefix("13940"));
Caveat: I'm not entirely sure the second method would not give you the same problem though.
With the the plethera of awnsers and you stating it needs to be more generic and you need the prefix and suffix avaliable to the Select, OrderBy, & GroupBy keywords, you should have the prefix and suffix in two different fields.
Foo Table
----------
Prefix | Suffix
----------------
10245 | 05
With that, you can query them individually to get what you want:
var resultSet = Db.Foo.Where(x => x.Suffix == "05").OrderBy(x => x.Prefix);
With this you can easily add a read-only property to get a formatted value:
public [partial] class Foo {
//Your other code
public string FormattedValue {
get { return Prefix + "-" + Suffix; }
}
}
You can use .StartsWith to check the prefix:
string prefix = "13940";
var result = context.FooTable.Where(w => w.Foo.StartsWith(prefix + "-"));
and .EndsWith to check the suffix:
string suffix = "02";
var result = context.FooTable.Where(w => w.Foo.EndsWith("-" + suffix));
You could create custom getters in your Foo class:
public class Foo
{
//your code
[NotMapped]
public string Prefix { get { return /*whatever*/; }
}
I think this should work.
You cannot filter your database selection based on a custom function - as you say, it cannot convert this to SQL.
For this specific problem, I could propose you use the StartsWith function, which does work on SQL server
context.FooTable.Where(w => w.Foo.StartsWith("13940"));
Use Microsoft.Linq.Translations.
It would look something like this:
partial class FooTable
{
private static readonly CompiledExpression<FooTable,string> prefixExpression
= DefaultTranslationOf<FooTable>.Property(e => e.Prefix).Is(e => e.Foo.Substring(0, 5));
public string Prefix
{
get { return prefixExpression.Evaluate(this); }
}
}
And queried like:
context.FooTable.Where(w => w.Prefix == "13940").WithTranslations();
Nuget gallery page
Documentation
EDIT: This solution works in Select, GroupBy, OrderBy.
I currently have a LINQ expression for a property accessor that accesses a property on an object. I now need to build a LINQ expression that will evaluate a predicate to see if the result from the property accessor is contained within a list.
I've seen that in most cases this kind of thing is done using the static methods on Expression, however there is no Contains or In available as a static method on Expression, so I am unsure how to proceed.
// A data object
internal class PersonDAL
{
public int Age ( get; set; }
}
// A business object
public class Person
{
private PersonDAL root { get; set; }
public static Expression<Func<PersonDAL, int>> AgeExpression
{
get
{
return (root) => (root.Age);
}
}
}
Now I want to be able to check if the value of AgeExpression is contained within a list of ages. Normally this would be something like writing an expression to see if the list of values contains the value that I want to check for, but I don't see how to feed the result of an expression in as the value to search for.
To clarify a little, I'm trying to figure out how to take a queryable that has all Persons and get just the Persons where an unknown expression evaluates true based on a value from another expression. In my sample case, the unknown accessor expression is looking at the Age of a Person and it needs to be able to evaluate if it is contained within another list of acceptable ages.
I'm not sure why you're using expressions. I don't see what you gain by them. But if you modify the signature of AgeExpression to be Expression<Func<PersonDAL, int>>, you can compile it and execute it:
void Main()
{
var pDal = new PersonDAL { Age = 3 };
var ageFunc = Person.AgeExpression.Compile();
var age = ageFunc(pDal);
// age is 3
}
// Define other methods and classes here
// A data object
public class PersonDAL
{
public int Age { get; set; }
}
// A business object
public class Person
{
public Person(PersonDAL dal)
{
this.dal = dal;
}
private PersonDAL dal { get; set; }
public static Expression<Func<PersonDAL, int>> AgeExpression
{
get
{
return (root) => (root.Age);
}
}
}
This kind of problem foxed me a lot too.. remember that you can access local variables within your LINQ/lambda expressions: so a simple .where(x => x.value == AgeExpression) should point you in the right direction
I stumbled trying to use my specification inside a LINQ query. The trouble here is with my specification with params.
Let's fake a simple scenario:
public class Car {
public Guid Id { get; set; }
public string Color { get; set; }
public int UsedPieces { get; set; }
// whatever properties
}
public class Piece {
public Guid Id { get; set; }
public string Color { get; set; }
// whatever properties
}
public static class PieceSpecifications : ISpecification<Piece> {
public static ISpecification<Piece> WithColor(string color) {
return new Specification<Piece>(p => p.Color == color);
}
}
what I'm actually trying to do
// Get accepts ISpecification and returns IQueryable<Car> to force just one call to database
var carWithPieces = _carRepository.Get(CarSpecifications.UsedPiecesGreaterThan(10));
var piecesWithColor = from p in _pieceRepository.Get()
let car = carWithPieces.FirstOrDefault() // entire query will does one call to database
where PieceSpecifications.WithColor(car.Color).IsSatisfiedBy(p) // unfortunately it isn't possible
// where p.Color == car.Color -> it works, but it's not what I want
select p;
I know it's a little bit confusing, but I'm trying to avoid a lot of roundtrips inside my real(big) scenario and I know that actually it's impossible to do using raw LINQ with entity framework. I'm tired to try so many blogs and failed(mine) approaches.
Someone knows some real good approach. There's another way to do that?
Error
System.NotSupportedException: LINQ to Entities does not recognize the
method 'Boolean IsSatisfiedBy(App.Model.Piece)' method, and this
method cannot be translated into a store expression.
UPDATE
Basic Specification Pattern
public class Specification<T> : ISpecification<T> {
private readonly Expression<Func<T, bool>> _predicate;
public Specification(Expression<Func<T, bool>> predicate) {
_predicate = predicate;
}
public Expression<Func<T, bool>> Predicate {
get { return _predicate; }
}
public bool IsSatisfiedBy(T entity) {
return _predicate.Compile().Invoke(entity);
}
}
UPDATE
It's pretty easy neat if I do this
// call to database
var car = _carRepository
.Get(CarSpecifications.UsedPiecesGreaterThan(10))
.FirstOrDefault();
// Whoah! look I'm working, but calling to database again.
var piecesWithColor = _pieceRepository
.Get(PieceSpecifications.WithColor(car.Color))
.ToArray();
Repository
// The Get function inside repository accepts ISpecification<T>.
public IQueryable<T> Get(ISpecification<T> specification) {
return Set.Where(specification.Predicate);
}
You can't compile and invoke expression if you want to use it in LINQ-to-entities query. Try to use Predicate directly because LINQ-to-entities builds expression tree which is evaluated by EF LINQ provider and translated to SQL.
IMHO using specification this way doesn't make sense. LINQ-to-entities query is a composite specification. So either use Linq-to-entities or build your own query language using specification and let your repository translate your query to LINQ query.
Take a look at using AsExpandable extension method.
http://www.albahari.com/nutshell/linqkit.aspx
Maybe make IsSatisfiedBy() and extension method to IQueryable. Here is K. Scott Allen's approach:
http://odetocode.com/Blogs/scott/archive/2012/03/19/avoiding-notsupportedexception-with-iqueryable.aspx
I have a class which contains a generic dictionary:
protected Dictionary<K,T> Data { get; set;}
I wish to allow clients of this class to perform LINQ queries without having to return the Dictionary. I had look at AsQueryable() but that doesn't seems to do what I wish.
An example of the code I would like to write would be:
typeRepo.Query().Where( x => x.name == "wire")
It seems like the dictionary is holding a model (T) by its key (K). The code you'll need is:
public IQueryable<T> Query
{
get { return Data.Values.AsQueryable(); }
}
Just add a public property like that:
public IEnumerable<KeyValuePair<K,T>> Query
{
get { return Data.AsEnumerable(); }
}
and it should work like you expect.