Use reflection and lambda expression with linq - c#

I need to filter a collection of items by verifing the value of a flag named deletion_date
public List<T> GetAll()
{
if (context == null) context = new ajtdevEntities();
return context.Set<T>().Where(p => p.GetType().GetProperty("deletion_date") == null).ToList();
}
I get an exception when I used this generic method
LINQ to Entities does not recognize the method ' System.Reflection.PropertyInfo GetProperty ( System.String )' , and the latter can not be translated into term store.
How can I fix this method?

Instead of reflection, you can build the filter expression manually using System.Linq.Expressions like this:
public List<T> GetAll<T>()
{
var parameter = Expression.Parameter(typeof(T), "p");
var predicate = Expression.Lambda<Func<T, bool>>(
Expression.Equal(Expression.PropertyOrField(parameter, "deletion_date"), Expression.Constant(null)),
parameter);
if (context == null) context = new ajtdevEntities();
return context.Set<T>().Where(predicate).ToList();
}
Note that the above will throw exception if your type does not have property/field called "deletion_date" or the type of the property does not support null. But the same can be said for your reflection based implementation (if it worked).

An ORM will inspect the lambda and convert its parts to SQL. The Entity Framework team chose not to support reflection, and rightfully so. So it can't translate GetProperty() calls to SQL, hence the error you get.
It wouldn't work anyway, because GetProperty() gets a PropertyInfo instance, not the value. So if it were null, that would indicate that the type of p has no property named deletion_date.
The proper way would be to call GetValue() on the PropertyInfo (note that this will throw a NullReferenceException if there is no property named thusly):
p => p.GetType().GetProperty("deletion_date").GetValue(p)
But again, reflection is not supported in Entity Framework, so you want to use interfaces:
public interface IDeletable
{
DateTime? deletion_date { get; set; }
}
And apply that to your class or method as a generic constraint. Then you can use it in your lambda:
public class WhateverClass<T>
where T : IDeletable
{
public List<T> GetAll()
{
return context.Set<T>().Where(p => p.deletion_date == null).ToList();
}
}

Related

passing entity type as parameter in linq

How would I go about passing an entity type as a parameter in linq?
For e.g. The method will receive the entity name value as a string and I would like to pass the entity name to the below linq query. Is it possible to make the linq query generic ?
public ActionResult EntityRecords(string entityTypeName)
{
var entityResults = context.<EntityType>.Tolist();
return View(entityResults);
}
I would like to pass the Entity type as a parameter and return all property values.
Also, is it possible to filter results based the some property?
Assuming your context class is looking like this:
public class MyContext : DbContext
{
public DbSet<Entity1> Entity1 { get; set; }
public DbSet<Entity2> Entity2 { get; set; }
// and so on ...
}
simplest solution is to write method that looks like
private List<object> Selector(string entityTypeName)
{
if (entityTypeName == "Entity1")
return context.Entity1.ToList();
if (entityTypeName == "Entity2")
return context.Entity2.ToList()
// and so on
// you may add a custom message here, like "Unknown type"
throw new Exception();
}
But we don't want to hardcode this stuff, so let create Selector dynamically with Linq.Expressions
Define a Func field within your controller:
private readonly Func<string, List<object>> selector;
Now you can create a factory for this member:
private Func<string, List<object>> SelectByType()
{
var myContext = Expression.Constant(context);
var entityTypeName = Expression.Parameter(typeof(string), "entityTypeName");
var label = Expression.Label(typeof(List<object>));
var body = Expression.Block(typeof(MyContext).GetProperties()
.Where(p => typeof(IQueryable).IsAssignableFrom(p.PropertyType) && p.PropertyType.IsGenericType)
.ToDictionary(
k => Expression.Constant(k.PropertyType.GetGenericArguments().First().Name),
v => Expression.Call(typeof(Enumerable), "ToList", new[] {typeof(object)}, Expression.Property(myContext, v.Name))
)
.Select(kv =>
Expression.IfThen(Expression.Equal(kv.Key, entityTypeName),
Expression.Return(label, kv.Value))
)
.Concat(new Expression[]
{
Expression.Throw(Expression.New(typeof(Exception))),
Expression.Label(label, Expression.Constant(null, typeof(List<object>))),
})
);
var lambda = Expression.Lambda<Func<string, List<object>>>(body, entityTypeName);
return lambda.Compile();
}
and assign Func with it (somewhere in constructor)
selector = SelectByType();
Now you can use it like
public ActionResult EntityRecords(string entityTypeName)
{
var entityResults = selector(entityTypeName);
return View(entityResults);
}
You have two options:
Option 1: You know the entity type at compile time
If you know the entity type at compile time, use a generic method:
public ActionResult EntityRecords<TEntity>()
{
var entityResults = context.Set<TEntity>.ToList();
return View(entityResults);
}
Usage:
public ActionResult UserRecords()
{
return EntityRecords<User>();
}
Option 2: You know the entity type only at runtime
If you actually want to pass the entity type as a string, use the other overload of Set that takes a type:
public ActionResult EntityRecords(string entityType)
{
var type = Type.GetType(entityType);
var entityResults = context.Set(type).ToList();
return View(entityResults);
}
This assumes that entityType is a fully qualified type name including assembly. See this answer for details.
If the entities are all inside the same assembly as the context - or in another well known assembly - you can use this code instead to get the entity type:
var type = context.GetType().Assembly.GetType(entityType);
This allows you to omit the assembly in the string, but it still requires the namespace.
You can achieve what you want even if the context doesn't have DbSet properties (and if it does, that doesn't harm). It is by calling the DbContext.Set<TEntity>() method by reflection:
var nameSpace = "<the full namespace of your entity types here>";
// Get the entity type:
var entType = context.GetType().Assembly.GetType($"{nameSpace}.{entityTypeName}");
// Get the MethodInfo of DbContext.Set<TEntity>():
var setMethod = context.GetType().GetMethods().First(m => m.Name == "Set" && m.IsGenericMethod);
// Now we have DbContext.Set<>(), turn it into DbContext.Set<TEntity>()
var genset = setMethod.MakeGenericMethod(entType);
// Create the DbSet:
var dbSet = genset.Invoke(context, null);
// Call the generic static method Enumerable.ToList<TEntity>() on it:
var listMethod = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(entType);
var entityList = listMethod.Invoke(null, new[] { dbSet });
Now you've got your list of entities.
One remark: To get rid of some performance impact due to reflection you could cache some types and non-generic method infos.
Another remark: I don't think I would recommend this. As said in a comment: this raises a couple of concerns. For example: are you going to allow a client application to get all unfiltered data of any entity table? Whatever it is you're doing: handle with care.
In your example, it looks like you have a controller action that's taking the entity name as a parameter, so you won't be able to make your method generic. But you can use reflection and avoid the use of generics for the most part.
public ActionResult EntityRecords(string entityTypeName)
{
var entityProperty = context.GetType().GetProperty(entityTypeName);
var entityQueryObject = (IQueryable)entityProperty.GetValue(context);
var entityResults = entityQueryObject.Cast<object>().ToList();
return View(entityResults);
}
There are a few things to keep in mind, though:
The assumption is that you've got a property on your context corresponding to the given entityTypeName argument. If entityTypeName is actually the type name instead of the property name, you'll need to do extra work to find the appropriate property.
Your View will have to know what to do with a collection of objects where the type of the objects is not known at compile time. It'll probably have to use reflection to do whatever you intend for it to do.
There may be some security concerns in a method like this. For example, if the user provides "Database" or "Configuration", you could end up exposing information like your connection string, which has nothing to do with the actual entities you've stored.
Also, is it possible to filter results based the some property?
Yes, and it will involve a similar use of reflection and/or dynamic. You could use a library like Dynamic LINQ to pass strings into LINQ-like method overloads (Where, Select, etc.).
public ActionResult EntityRecords(string entityTypeName, FilterOptions options)
{
var entityProperty = context.GetType().GetProperty(entityTypeName);
var entityQueryObject = entityProperty.GetValue(context);
var entityResults = ApplyFiltersAndSuch((IQueryable)entityQueryObject);
return View(entityResults);
}
private IEnumerable<object> ApplyFiltersAndSuch(IQueryable query, FilterOptions options)
{
var dynamicFilterString = BuildDynamicFilterString(options);
return query.Where(dynamicFilterString)
// you can add .OrderBy... etc.
.Cast<object>()
.ToList();
}

Use IQueryable.OrderBy for a composite key without loading all the items?

I am retrieving some tuples from a database that are mapped to entity classes by means of Entity Framework.
For these entities, I have a key selector function (supplied at runtime by other developers) that I would like to pass to Queryable.OrderBy. The key selector function is provided upon "registration" of the entity type in my system - which happens by means of a method that looks roughly like this:
public void RegisterEntity<TEntity, TKey>(string entityName, TKey defaultKey, Func<TEntity, TKey> keySelectorFunc)
I would like to execute this OrderBy call before materializing the results to entity objects (i.e. in such a way that the OrderBy call still gets translated to SQL under the hood).
The problem is that the entities have composite keys, and thus, the key selector function will return a custom object instantiated in the function. You can imagine it like this:
var keySelectorFunc = e => new CustomKey(e.Value1, e.Value2);
As usual, Entity Framework does not like this (the usual "Only parameterless constructors and initializers are supported in LINQ to Entities" error).
Is there any way to use such a custom key selector function to return a custom key? Do I have to resort to anonymous classes? Or should I move the OrderBy call to a place after I have left the LINQ-to-Entities world?
In this particular case it would be easy to use Sort method of Generic List.
https://msdn.microsoft.com/en-us/library/3da4abas(v=vs.110).aspx
Sort method requires the type of the list to implement IComparable interface and it uses the implementation of CompareTo method from IComparable interface. Otherwise implementation of IComparer also can be passed to this method.
So if your entity class is already implemeting IComparable interface then this should surely work for you. You will have to to .ToList() on the IQueryable result of course before you can call the Sort method on it.
public class Category : IComparable<Category>
{
public int CategoryId { get; internal set; }
public string CategoryName { get; internal set; }
public int CompareTo(Category x)
{
return String.Compare(x.CategoryName, this.CategoryName, StringComparison.InvariantCulture);
}
}
List<Category> categories = new List<Category>();
categories.Add(new Category {CategoryName = "Cate1"});
categories.Add(new Category {CategoryName = "Cate2"});
categories.Sort();
foreach (var cat in categories)
{
Console.WriteLine(cat.CategoryName);
}
This displays me category names in reverse order based on the comparison logic I have written in the CompareTo method of Category Class.
In this case I think the best way is use a custom ExtensionMethod to avoid any overhead of coding or unnecessary complexity to do that.
See if it implementation can help you.
First we create your customkey class that is responsable to create the statement expressions:
class CustomKey
{
public CustomKey(params string[] value)
{
if(!value.Any())
throw new InvalidOperationException("Select at least one Property for this operation");
Values = new List<string>();
Values.AddRange(value);
}
private List<string> Values { get; set; }
// this method run throughout all property configured to create the expressions
public void ForEachProperty<TSource, TKey>(Action<Expression<Func<TSource, TKey>>, bool> method)
{
bool firstItem = true;
Values.ForEach(f =>
{
var expression = CreateExpression<TSource, TKey>(f);
method(expression, firstItem);
firstItem = false;
});
}
// this method is responsable to create each expression
Expression<Func<TSource, TKey>> CreateExpression<TSource, TKey>(string property)
{
var parameter = Expression.Parameter(typeof(TSource), "x");
var member = typeof(TSource).GetMember(property).FirstOrDefault();
Expression body = Expression.MakeMemberAccess(parameter, member);
return Expression.Lambda<Func<TSource, TKey>>(Expression.Convert(body, typeof(object)), parameter);
}
}
After that we create your custom ExtesionMethod, somethink like that:
public static class OrderByExtensionClass
{
// instead of try passing an expression, we pass our CustomKey object with the columns to sort.
// than this method create the apropriate OrderBy Expression statement
public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, CustomKey customKey)
{
// the parameter isFirst is just to control where we are to build the expression
customKey.ForEachProperty<TSource, object>((expression, isFirst) =>
{
if (isFirst)
source = source.OrderBy(expression);
else
source = ((IOrderedQueryable<TSource>)source).ThenBy(expression);
});
return ((IOrderedQueryable<TSource>)source);
}
}
After that we just do:
CustomKey custom = new CustomKey("Name", "Age");
myEntityContext.People.OrderBy(custom).ToList()
I hope it can help you.
Part of the problem, I think, is that OrderBy wouldn't know what to do with a complex type. SQL Server knows how to order by primitive types, but that's about it. You would have to do something like ...OrderBy(x=>x.Field1).ThenBy(x=>x.Field2). You could write an extension method that takes the key, extracts the property names from the key, and builds the .OrderBy().ThenBy() expression, as long as you know what the key will be before executing the query. Otherwise yeah, you may have to materialize the results before ordering.

Traverse an expression tree and extract parameters

I'm writing a kind of a mapping tool. I have a method that looks like this (simplified):
public void RegisterMapping<TTarget, TSource>(string propertyName,
Expression<Func<TSource, object>> memberMap)
The memberMap is an expression defining how to transform a property from TSource to TTarget. For the business logic, I need to extract all references to properties of TSource from it. For example, from
x => x.Customers.Where(c => c.Orders.Any())
I would like to get Customers, and from
x => x.FirstName + " " + x.LastName
FirstName and LastName (could be as string[], PropertyInfo is trivial to convert to).
How would I do this? My first approach was to traverse the tree manually, checking the node type and inspecting different properties depending on the node type (e.g. Operand for unary expressions, Arguments for a function call) to determine of any of these are a property of TSource. Then I discovered the expression kind list and I gave up -- even if I support only the most common kinds, it's still lots of work. Then I found the ExpressionVisitor. It looks better, but it's still a lot of work to override the visitor methods and I'd like to know if there's an other option, using perhaps a more specialised framework, before I devote my time to this.
I think as you've said that using ExpressionVisitor works out to be a good approach. You don't need to implement all the Visit... methods as they already have a default implementation. From what I understood what you want is to find all property accesses of a certain type inside a lambda function
public class MemberAccessVisitor : ExpressionVisitor
{
private readonly Type declaringType;
private IList<string> propertyNames = new List<string>();
public MemberAccessVisitor(Type declaringType)
{
this.declaringType = declaringType;
}
public IEnumerable<string> PropertyNames { get { return propertyNames; } }
public override Expression Visit(Expression expr)
{
if (expr != null && expr.NodeType == ExpressionType.MemberAccess)
{
var memberExpr = (MemberExpression)expr;
if (memberExpr.Member.DeclaringType == declaringType)
{
propertyNames.Add(memberExpr.Member.Name);
}
}
return base.Visit(expr);
}
}
This could be further improved to what you want by checking the member is a property and also to get PropertyInfo rather than strings
It could be used as follows:
var visitor = new MemberAccessVisitor(typeof(TSource));
visitor.Visit(memberMap);
var propertyNames = visitor.PropertyNames;

Nhibernate Linq Get<T> object by name not by Id

public T FindById(object id)
{
return session.Get<T>(id);
}
I want to get object by Name
Something like this, but I am not able to write a query with type T
session.Get<T>().Where(x => x.something == something).SingleOrDefault();
or is there any alternative to this approach?
You can use Expression trees and build something like:
protected T Get<T, TValue>(string propertyName, TValue value) where T : class
{
var par = System.Linq.Expressions.Expression.Parameter(typeof(T), "x");
var eq = System.Linq.Expressions.Expression.Equal(System.Linq.Expressions.Expression.Property(par, propertyName), System.Linq.Expressions.Expression.Constant(value));
var lambda = System.Linq.Expressions.Expression.Lambda<Func<T, bool>>(eq, par);
return session.QueryOver<T>().Where(lambda).SingleOrDefault();
}
Note that I'm using the full namespace + classname because NHibernate has another Expression class.
There is a native solution in NHiberante:
Chapter 15. Criteria Queries
This Criteria API (which is there from the beginning) is really what we need here. For example the QueryOver API - is built on top of that.
The biggest benefit is, it does work natively with "strings". This would be very simple, easy to maintain piece of code:
public T GetByProperty<T>(string propertyName, object value)
where T: class
{
var session = ... // get a session
return session.CreateCriteria<T>()
.Add(Restrictions.Eq(propertyName, value))
.SetMaxResults(1)
.List<T>()
.FirstOrDefault();
}
There is a complete doc
You have to restrict T to something with a particular property. You could, for instance, create a superclass called NamedObject which has a property Name and make all nameable objects inherit from it. Restricting T to a NameableObject or descendant would then allow you to access the Name property in your query.
Not particularly elegant but it should work.
I think you should use reflection for query a generic type, just like this:
session.Get<T>().Where(P => typeof(P).GetPropery("PropertyName").GetValue(P).ToString() == "Something").SingleOrDefault();
In this code, typeof(oObject) returns de a Type intance of the object, then the GetProperty("PropertyName") method returns you a System.Reflection.PropertyInfo with that you can get the property value with GetValue(P) method that receive an instance of the object that contain the property in this case is [P] create in Lamba expression.
Im not sure what you mean by "I am not able to write a query with type T".
But you can call with expression where refactor is supported.
public T GetByExpression<T>(Expression<Func<T, bool>> restriction) where T : class
{
return Session.QueryOver<T>().Where(restriction).SingleOrDefault();
}

C# Linq generic search

I am trying to write a method for a base repository service. The idea is I want a generic class that can be used for simple entity types hat can also be overridden for more complex entities. I am writing a search method with the idea being that for simple entities there will be a component which will have one or more properties with fields which match properties of the entity. if these are found then build a where linq statement to query it. Here's what I have so far:
public IQueryable<T> GetAll()
{
return entityRepository.GetAll();
}
public IQueryable<T> Search(IBaseComponent component)
{
IQueryable<T> all = GetAll();
Type type = typeof(T);
Type componentType = component.GetType();
foreach (var componentProperty in componentType.GetProperties())
{
foreach (var property in type.GetProperties())
{
if (property.Name.Equals(componentProperty.Name))
{
var value = componentProperty.GetValue(component);
ParameterExpression gpe = Expression.Parameter(property.DeclaringType, "a");
var selector = Expression.Equal(Expression.Property(gpe, property), Expression.Constant(value));
var keySelector = Expression.Lambda(selector, gpe);
var t = all.Where(keySelector);
break;
}
}
}
var test = all.ToArray();
return all;
}
Obviously this would only theoretically work for one property at the moment. The service itself has a type parameter (so its BaseService<T>).
The problem I am having is that this will not compile. The line all.Where(keySelector) gives the error:
'System.Linq.IQueryable<T>' does not contain a definition for 'Where' and the best extension method overload 'System.Linq.Enumerable.Where<TSource>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,int,bool>)' has some invalid arguments
I'm not sure what's wrong here, they types are all correct as far as I can see. What am I missing? Or am I trying to do something impossible?
Expression.Lambda return type is LambdaExpression even if the concrete type is Expression<Func<T, bool>> (LambdaExpression is the non generic base class for Expression<T>)
#Euphoric remark is spot on and the var keyword is the problem here as the reason for this error would have been obvious without it.
The correct code is something like :
var keySelector = (Expression<Func<T, bool>>)Expression.Lambda(selector, gpe);

Categories