Selecting distinct entity values based on string field name - c#

Is it possible to select distinct property values of an entity where the column name is not known in advance - received by the method as a string from the client.
For filtering purposes I want to make a HTTP POST Ajax request to the server from the browser which will contain the field name as a key, and this will be available to the server as a string.
I know I am going to have to manually map any differences between the ViewModel and the POCO class used by Entity Framework, but is it possible to construct an Entity framework query without the strongly typed property - by using reflection for example.
I'd probably try to implement this where the data was determined by controller, calling a generic method of the base repository with the entity class as the type. Is this possible or do I need to construct a method for each possible field?
Equally, should I be attempting to do this, or instead constructing ADO.NET SQL commands with the field as a parameter (mapping from ViewModel to SQL column names)?

I'm pretty sure I saw a similar answer recently but I can't find it...
If you know the type of your entity and the type of the property you're going to be selecting, this is pretty easy:
public IQueryable<TProperty> SelectProperty<TEntity, TProperty>(DbContext context, string propertyName)
where TEntity : class
{
var parameter = Expression.Parameter(typeof(TEntity));
var body = Expression.Property(parameter, propertyName);
var lambda = Expression.Lambda<Func<TEntity, TProperty>>(body, parameter);
var result = context.Set<TEntity>().Select (lambda).Distinct();
return result;
}
If you can't predict the type of the property then building the expression will be more difficult:
public IQueryable SelectProperty<TEntity>(DbContext context, string propertyName)
where TEntity : class
{
var entities = context.Set<TEntity>();
var query = entities.AsQueryable();
var parameter = Expression.Parameter(typeof(TEntity), "instance");
var propertyAccess = Expression.Property(parameter, propertyName);
var projection = Expression.Lambda(propertyAccess, parameter);
var selectExpression = Expression.Call(
typeof(Queryable).GetMethods()
.First (x => x.Name == "Select")
.MakeGenericMethod(new[]{ typeof(TEntity), propertyAccess.Type }),
query.Expression,
projection);
var distinctExpression = Expression.Call(
typeof(Queryable).GetMethods()
.First (x => x.Name == "Distinct")
.MakeGenericMethod(new[]{ propertyAccess.Type }),
selectExpression);
var result = query.Provider.CreateQuery(distinctExpression);
return result;
}

You can use Dyanmic Linq (nuget package: System.Linq.Dynamic) to do this, along with an extension method which I will not even try to take credit for, found here
Sample code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace dlinq1
{
class Thing
{
public int ID { get; set; }
}
class Program
{
static void Main(string[] args)
{
var things = new List<Thing>();
for(int x=0;x<10;x++)
{
things.Add(new Thing{ID=x%2}); //0,1,0,1,0,1,0,1,0,1
}
var result = things.AsQueryable().Select("ID").Distinct();
foreach (var r in result)
{
Console.WriteLine(r);
}
Console.ReadLine(); //produces 0, 1
}
}
public static class DynamicQueryableExtras
{
public static IQueryable Distinct(this IQueryable q)
{
var call = Expression.Call(typeof(Queryable),
"Distinct",
new Type[] { q.ElementType },
q.Expression);
return q.Provider.CreateQuery(call);
}
}
}
Basically the extension works with whatever state the queryable is in since the previous expression just before you called Distinct.

Related

Error when making generic expression dynamically

Based on Generic expression abstraction issue I want to make a more generic method to use "UpdateGraph" using Entity Framework 6.
Currently, I can call SetOwnedCollectionMapping(t => t.ClassProperty), but I want to make a more reusable method by using the class instance and any class property.
Here is an example that I use :
private static void AddOrUpdate<T>(TDataEntity class1, T NavigationProperty) where T : class
{
PropertyInfo pinfo = typeof(TDataEntity).GetProperty("PropertyName");
object value = pinfo.GetValue(class1, null);
ParameterExpression pe = Expression.Parameter(value.GetType(), "L");
var arg = Expression.Constant(null, typeof(TDataEntity));
var body = Expression.PropertyOrField(arg, "PropertyName");
var lambda = Expression.Lambda(body);
SetOwnedCollectionMapping(lambda);
using (var db = new ConsoleContext())//
{
db.UpdateGraph(class1, graphMapping);
db.SaveChanges();
}
}
protected static Expression<Func<IUpdateConfiguration<TDataEntity>, object>> graphMapping { get; set; }
protected void SetOwnedCollectionMapping<T>(Expression<Func<TDataEntity, ICollection<T>>> mapping)
{
Expression<Func<IUpdateConfiguration<TDataEntity>, object>> template =
_ => _.OwnedCollection(mapping);
var map = Expression.Parameter(
typeof(IUpdateConfiguration<TDataEntity>),
"map");
graphMapping = Expression.Lambda<Func<IUpdateConfiguration<TDataEntity>, object>>(
Expression.Call(
((MethodCallExpression)template.Body).Method,
map,
Expression.Quote(mapping)),
map);
}
The error I have is on the call method SetOwnedCollectionMapping(lambda);
Error 1 The type arguments for method
'ConsoleApplication1.Program.SetOwnedCollectionMapping(System.Linq.Expressions.Expression>>)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.
I have doubt about my lambda expression. Can someone help me please ?
It looks like at AddOrUpdate you need to update following line to make compiler know what type are you going to pass there
SetOwnedCollectionMapping<T>(lambda);

MemberExpression as object of its type

I'm building Linq Extension methods.
Shortly, I've built an extension method in order to create a MemberExpression looks like:
public static Expression Field<T>(this object entity, string field)
{
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Property(parameterExpression, propertyInfo);
}
So, I'm able to do that:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").EndsWith(string.Empty)<<<<<<<< Compilation error.
);
Since MemberExpression have not EndsWith method, I'm not able to extend this MemberExpression like a String property access like:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Matter.EndsWith(string.Empty)
);
Is there some way to do that.
As you are able to figure out I'm trying to get something a bit more complex, Nevertheless, this example is for explaining the situation.
I hope it's enought clear.
Scope
My UI is using a backend.
This backend have three implementations. Each one of them provides a Linq implementation (Linq collections, NHibernate, custom-made Linq provider).
So, my UI is able to work on collections, a database or getting data from our server.
I'd like to provide util extension methods like AnyField().
So, after digging a bit I'm thinking on two approaches:
AnyField() generates an expression tree which is able to be translated by every Linq provider (first answer of this post).
Provide a default implementation of Anyfield() for Linq Collections, and then use each Linq provider extension mechanism for handle it. Or, if you are building a Linq Provider, support it on implementation.
Okay, so you're getting tripped up on the syntactic sugar that C# provides for you when building ExpressionTrees
Where expects Expression<Func<TObjectType, TReturnType>> or a compiled lambda; Func<TObjectType, TReturnType>.
Your method Field currently only returns an untyped Expression. That means your query is actually returning Expression<Func<TObjectType, Expression>>. That's not right! It should be returning a Expression<Func<TObjectType, string>>! But how do we do that? That would mean our method would have to return a string, but we want to build an expression tree.
To get it working as you're expecting, it's quite a bit more difficult than you would imagine, but that's only because we're so spoiled with the syntactic sugar.
What we actually need to do is write methods which accept lambda methods, and return lambda methods, each one re-writing the body a little bit.
So... what does that look like?
public static Expression<Func<TElementType, object>> Field<TElementType, TReturnType>(this Expression<Func<TElementType, TReturnType>> expr, string field)
{
Type entityType = expr.Body.Type;
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Lambda<Func<TElementType, object>>(
Expression.Property(parameterExpression, propertyInfo),
parameterExpression
);
}
Notice that it's almost the exact same as what you wrote, but we wrap it with Lambda<Func<TElementType, TReturnType>>. And the signature is a bit different too.
Instead of operating on an object, we want to operate on a lambda expression. We also return a lambda expression.
So how do we use it?
var classes = objects.Where(
ExpressionExtensions.Field<Test, Test>(q => q, "Matter")
);
Great! Now we're passing Expression<Func<Test, string>> to Where, rather than Expression<Func<Test, MemberExpression>>. Making progress.
But that won't compile, and rightly so. We're returning a string, but we're using a filtering method, which requires a bool.
So let's now write EndsWith:
public static Expression<Func<T, bool>> EndsWith<T, TReturnType>(this Expression<Func<T, TReturnType>> expr, string str)
{
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var newBody = Expression.Call(expr.Body, endsWithMethod, Expression.Constant(str));
var result = Expression.Lambda<Func<T, bool>>(newBody, expr.Parameters);
return result;
}
And using it:
var classes = objects.Where(
ExpressionExtensions.Field<Test, Test>(q => q, "Matter")
.EndsWith("A")
);
Which is now compiling! And the expression tree looks like this:
UserQuery+Test[].Where(e => e.Matter.EndsWith("A"))
That's not too pretty, having Field take a redundant lambda, though. Let's add a helper method to make it look prettier:
public static Expression<Func<TElementType, TElementType>> Query<TElementType>(this Expression<Func<TElementType, TElementType>> expr)
{
return expr;
}
Putting it all together:
void Main()
{
var objects = new[] { new Test { Matter = "A" } }.AsQueryable();
var classes = objects.Where(
ExpressionExtensions.Query<Test>(q => q)
.Field("Matter")
.EndsWith("A")
);
classes.Expression.Dump();
}
public class Test
{
public string Matter { get; set;}
}
public static class ExpressionExtensions
{
public static Expression<Func<TElementType, TElementType>> Query<TElementType>(this Expression<Func<TElementType, TElementType>> expr)
{
return expr;
}
public static Expression<Func<TElementType, object>> Field<TElementType, TReturnType>(this Expression<Func<TElementType, TReturnType>> expr, string field)
{
Type entityType = expr.Body.Type;
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
return Expression.Lambda<Func<TElementType, object>>(
Expression.Property(parameterExpression, propertyInfo),
parameterExpression
);
}
public static Expression<Func<T, bool>> EndsWith<T, TReturnType>(this Expression<Func<T, TReturnType>> expr, string str)
{
var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var newBody = Expression.Call(expr.Body, endsWithMethod, Expression.Constant(str));
var result = Expression.Lambda<Func<T, bool>>(newBody, expr.Parameters);
return result;
}
}
I don't know if you mess the code to show a piece of code or if it was intended, but you have a generic extension that wants A T but you don't use it
Anyway if what you want is a method that returns you the value of a property, why don't you do a static exception that return T ?
public static class EntityExtension {
public static T Field<T>(this object entity, string field) {
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null) {
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
}
return (T)propertyInfo.GetValue(entity);
}
}
this is a fiddle i've done to show you the usage, pretty simple
https://dotnetfiddle.net/PoSfli
posting the code too in case fiddle get lost:
using System;
using System.Reflection;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
YourClass c = new YourClass() {
PropA = 1,
PropB = 2,
PropC = "ciao"
};
var propBValue = c.Field<int>("PropB");
Console.WriteLine("PropB value: {0}", propBValue);
var propCValue = c.Field<string>("PropC");
Console.WriteLine("PropC value: {0}", propCValue);
}
}
public static class EntityExtension {
public static T Field<T>(this object entity, string field) {
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null) {
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
}
return (T)propertyInfo.GetValue(entity);
}
}
public class YourClass {
public int PropA { get; set; }
public int PropB { get; set; }
public string PropC { get; set; }
}
nota that you can improve a lot, using a typed extension and a property expression as argument instead of a string
You can also do something really simple like if you want to use the property name:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").ToString().EndsWith(string.Empty)
Or if your are filtering by property type:
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").Type.ToString().EndsWith(string.Empty)
How about something like this? Actually, your generic approach is not of use right now.
public static bool Evaluate<TField>(this object entity, string fieldName, Predicate<TField> condition)
{
Type entityType = entity.GetType();
PropertyInfo propertyInfo = entityType.GetProperty(field);
if (propertyInfo == null)
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));
var value = (TField)propertyInfo.GetValue(entity); //read the value and cast it to designated type, will raise invalid cast exception, if wrong
return condition.Invoke(value); //invoke the predicate to check the condition
}
Usage would be then.
.Where(item => item.Evaluate<string>("Matter", prop => prop.EndsWith(string.Empty))
You can add a new extension method which returns your desired type.
public static T Compile<T>(this Expression expression)
{
return Expression.Lambda<Func<T>>(expression).Compile()();
}
In your statement you just have to add .Compile<type>()
IEnumerable<C> classes = this.backend.cs.Where(
c => c.Field<C>("Matter").Compile<string>().EndsWith(string.Empty));

Converting Expression<Func<DTO,bool>> to Expression<Func<Domain, bool>>

I'm using Expression Tree Serializer for sending Expression< FuncDTO,bool>> to WCF services and
my repository is working with Domain type. I want to apply this expression for my repository
Expression<Func<UserDto,bool>> expression = new ExpressionSerializer().Deserialize<Func<UserDto, bool>>(xmlElement);
var addressBookEntries = addressBooksRepository.Where(expression); //accepts Expression<Func<UserDomain,bool>>
How can I fix this problem?
I used dynamic linq and it solved my problem, only little issue property names must be equals. I converted dynamic linq to string and sent it thorough wcf service and using it as string
using System.Linq;
using System.Linq.Dynamic;
public ICollection<UserDto> GetUsersByFilter(string filter)
{
var addressBooksRepository = new AddressBooksRepository();
var addressBookEntries = addressBooksRepository.GetAll().Where(filter);
//return data
}
Try create new expression with your type arguments based on existed one.
public class UserDto : UserDomain
{ }
public class UserDomain
{ }
void YourMethod()
{
Expression<Func<UserDto, bool>> expression = new ExpressionSerializer().Deserialize<Func<UserDto, bool>>(xmlElement);
Func<UserDto, bool> func = expression.Compile();
Expression<Func<UserDomain, bool>> newExpression = x => func(x as UserDto);
var addressBookEntries = addressBooksRepository.Where(newExpression);
}

Entity framework - get entity by name

I have the following code (example):
public dynamic GetData(string name)
{
using(var ctx = GetObjectContext())
{
switch (name)
{
case "entity1":
return ctx.entity1.ToList();
case "entity2":
return ctx.entity2.ToList();
......
default:
return null;
}
}
}
I want to avoid switch in this sample. How can I find needed entity class by name, call the ToList() method and return data? Can I do this using reflection?
You can do it using reflection, however you will also need to use generics because the type of list returned by the ToList() method is different for each entity type.
You can access a property getter through reflection like so:
var enumerable = typeof([ClassNameOfContext]).GetProperty(name).GetValue(ctx, null);
Whereas [ClassNameOfContext] is the name of the class that ctx is an instance of. This is not obvious from your code, but you know it :-)
The problem is that enumerable will be an object and has to be casted to IEnumerable<EntityType> where EntityType is the type of entity you are accessing. In other words, it depends on the name you are passing. If you use generics to determine the type, you will be able to properly cast the object and don't have to return a dynamic even.
public TEntity Get<TEntity>(string name)
{
...
and transform the line from above:
var enumerable = (IEnumerable<TEntity>)(typeof([ClassNameOfContext]).GetProperty(name).GetValue(ctx, null));
return enumerable.ToList();
here you go!
Addendum: You could, conceivably, get rid of the string parameter, too - having names of types or properties in strings should be avoided where possible because it is not type safe. The compiler does not recognize it, and IDE features such as refactorings don't account for it. The problem here is that the property names are usually the pluralized form of the entity type names. But you could use reflection to find the property whose type matches the TEntity. I leave this as an exercise :-)
You can use code like this
private IEnumerable<TEntity> GetList<TEntity>(string connectionString, Func<object, T> caster)
{
using (var ctx = new DbContext(connectionString))
{
var setMethod = ctx.GetType().GetMethod("Set").MakeGenericMethod(typeof(T));
var querable = ((DbSet<object>)setMethod
.Invoke(this, null))
.AsNoTracking()
.AsQueryable();
return querable
.Select(x => caster(x))
.ToList();
}
}
To call like this:
var branchList = GetList<Branch>("connectionStringName", x => (Branch)x);
You can remove .AsNoTracking() and remove .ToList(), then you will get pure IQueryable which you can query further.
I've created a method to include all related entities with some help of the great answer of #chiccodoro.
using the entity "Product" which has 7 navigation properties
public static IQueryable<T> IncludeAllEntities<T>(this DbSet<T> entity, DataContext context) where T : class
{
var querable = entity.AsQueryable();
var type = typeof(T);
var entityType= context.Model.FindEntityType(type);
var navs = entityType?.GetNavigations();
if (navs==null)
{
return null;
}
List<string> navNames = new List<string>();
foreach (var nav in navs)
{
navNames.Add(nav.Name);
}
try
{
var agg = navNames.Aggregate(querable, (acc, name) => acc.Include(name));
return agg;
}
catch (Exception ex)
{
throw;
}
}
here I am getting the type of the entity to get the navigation properties, then adding the name of each one to a list of string, then aggregating over the list to include each entity.
then we can use this extension method like this:
var record= await _context.Products.IncludeAllEntities(_context).FirstOrDefaultAsync(x => x.Id == key);

Create an Expression<Func<,>> using reflection

Im using Moq to create mocks of a data set.
I have created a little helper class that allows me to have an in memory storage instead of a database that makes unit testing a breeze. That way I can add and remove items from my mock data set, this allows me to test my insert and delete service calls.
During the setup of the mock I have a line that looks like the following
this.Setup(i => i.AcademicCycles).Returns(mockStore.GetList<AcademicCycle>());
My mock has a lot of properties so I would like to perform this setup step using reflection. I have managed to the Returns part of the process working via reflection but I am stuck on the lambda method to Setup.
Setup takes an
Expression<Func<GoalsModelUnitOfWork, IQueryable<AcademicCycle>>> that corresponds to the i => i.AcademicCycles
and I would like to create this dynamically. Using reflection I have the following:
The name of the property: "AcademicCycles"
The type IQueryable<AcademicCycle>
The type AcademicCycle
I also have the instance of the i in the lambda statement which is a GoalsModelUnitOfWork
The code to create the expression dynamically would be like this:
ParameterExpression parameter = Expression.Parameter(typeof (GoalsModelUnitOfWork), "i");
MemberExpression property = Expression.Property(parameter, "AcademicCycles");
var queryableType = typeof (IQueryable<>).MakeGenericType(typeof (AcademicCycle));
var delegateType = typeof (Func<,>).MakeGenericType(typeof (GoalsModelUnitOfWork), queryableType);
var yourExpression = Expression.Lambda(delegateType, property, parameter);
The result will have the desired type, but the problem is that the return type of Expression.Lambda() is LambdaExpression and you can't perform a type cast to Expression<Func<...>> to pass it as parameter to your setup function because you don't know the generic type parameters for the Func. So you have to invoke the Setup method by reflection, too:
this.GetType().GetMethod("Setup", yourExpression.GetType()).Invoke(this, yourExpression);
I decided to take a crack at it and ended up with this god awful piece of code.
I am no reflection expert and this is just a first attempt to get something working. I'd be very interested in what other approaches people have, or whether any of the relfection wrapper libraries can make this nicer.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Moq;
using Xunit;
namespace MyExample
{
public class Tests
{
[Fact]
public void Test()
{
Dictionary<Type, object> data = new Dictionary<Type, object>
{
{ typeof(IQueryable<Cycle>), new List<Cycle> { new Cycle { Name = "Test" } }.AsQueryable() },
{ typeof(IQueryable<Rider>), new List<Rider> { new Rider { Name = "1"}, new Rider { Name = "2" } }.AsQueryable() }
};
var mock = new Mock<IDataContext>();
var setup = mock.GetType().GetMethods().Single(d => d.Name == "Setup" && d.ContainsGenericParameters);
var param = Expression.Parameter(typeof(IDataContext), "i");
foreach (var property in typeof(IDataContext).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
// Build lambda
var ex = Expression.Lambda(Expression.MakeMemberAccess(param, property), param);
// Get generic version of the Setup method
var typedSetup = setup.MakeGenericMethod(property.PropertyType);
// Run the Setup method
var returnedSetup = typedSetup.Invoke(mock, new[] { ex });
// Get generic version of IReturns interface
var iReturns = typedSetup.ReturnType.GetInterfaces().Single(d => d.Name.StartsWith("IReturns`"));
// Get the generic Returns method
var returns = iReturns.GetMethod("Returns", new Type[] { property.PropertyType });
// Run the returns method passing in our data
returns.Invoke(returnedSetup, new[] { data[property.PropertyType] });
}
Assert.Equal(1, mock.Object.Cycles.Count());
}
}
public class Cycle
{
public string Name { get; set; }
}
public class Rider
{
public string Name { get; set; }
}
public interface IDataContext
{
IQueryable<Cycle> Cycles { get; set; }
IQueryable<Rider> Riders { get; set; }
}
}
This method ought to construct the lambda expression. Since you are invoking the Setup method by reflection, you do not need a strongly-typed lambda expression; you are going to pass it as part of an object array when you call Invoke:
public LambdaExpression PropertyGetLambda(string parameterName, Type parameterType, string propertyName, Type propertyType)
{
var parameter = Expression.Parameter(parameterType, parameterName);
var memberExpression = Expression.Property(parameter, propertyName);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
return lambdaExpression;
}
I don't think you actually need the parameter name. If I'm right about that, you could simplify a bit:
public LambdaExpression PropertyGetLambda(Type parameterType, string propertyName, Type propertyType)
{
var parameter = Expression.Parameter(parameterType);
var memberExpression = Expression.Property(parameter, propertyName);
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
return lambdaExpression;
}

Categories

Resources