从实体框架中选择具有动态列名称的不同列

问题描述:

我正在寻找一种方法来获取不同值的列表,我的表的一列。我需要制定一个可重用的方法。从实体框架中选择具有动态列名称的不同列

这是我试过到目前为止,但它不工作:

IEnumerable<string> GetDistinctValues<T>(string columnName) 
{ 
    T.Select(m => m.ColumnName).Distinct().ToList(); 
} 

所需的解决方案应该是EF对象的扩展方法。

我试过这个帖子Dynamically select columns in runtime using entity framework,但它只适用于单个记录而不是列表。

+0

的可能的复制[使用实体框架在运行时动态地选择列] (https://stackoverflow.com/questions/21084916/dynamically-select-columns-in-runtime-using-entity-framework) –

+0

https://stackoverflow.com/a/31055926/3082296 – adiga

+0

任何你不做的原因选择的参数作为你的方法的参数?例如:IEnumerable GetDistinctValues (Func selector){T.Select(selector).Distinct()。ToList(); }'' –

Linq.Dynamic看到的唯一的问题是,有没有更新,自2013年起,该项目是非常死

我将通过扩展处理它,并通过缓存提高反射性能(这里就不详述了)

扩展:

public static class QueryableExtensions 
{ 
    public static IReadOnlyCollection<TResult> GetDistinctValuesForProperty<T, TResult>(this IQueryable<T> query, Expression<Func<T, TResult>> propertyAccess) 
    { 
     return SelectDistinct(query, propertyAccess).ToList(); 
    } 

    public static IReadOnlyCollection<object> GetDistinctValuesForProperty<TSource>(this IQueryable<TSource> query, string propertyName) 
    { 
     var unboundFuncType = typeof(Func<,>); 
     var unboundExprType = typeof(Expression<>); 

     var sourceType = typeof(TSource); // TSource 

     var resultType = typeof(TSource) 
      .GetProperty(propertyName) 
      .PropertyType; // TResult 

     // Func<TSource, TResult> 
     var funcType = unboundFuncType.MakeGenericType(new [] { sourceType, resultType }); 

     // Expression<Func<TSource, TResult>> 
     var expressionType = unboundExprType.MakeGenericType(new [] { funcType }); 

     // Instance of Expression<Func<TSource, TResult>>, for example x => x.Name 
     var propertyAccess = typeof(StringExtensions) 
      .GetMethod(nameof(StringExtensions.AsPropertyExpression), new[] { typeof(string) }) 
      .MakeGenericMethod(new [] { sourceType, resultType }) 
      .Invoke(null, new object[] { propertyName }); 

     // SelectDistinct query transform 
     var selectDistinctMethod = typeof(QueryableExtensions) 
      .GetMethod(nameof(QueryableExtensions.SelectDistinct), BindingFlags.NonPublic | BindingFlags.Static) 
      .MakeGenericMethod(new [] { sourceType, resultType }); 

     // IQueryable<TSource> ==> IQueryable<TResult> 
     var result = selectDistinctMethod.Invoke(null, new object[] { query, propertyAccess }); 

     // Cast to object via IEnumerable and convert to list 
     return ((IEnumerable)result).Cast<object>().ToList(); 
    } 

    private static IQueryable<TResult> SelectDistinct<TSource, TResult>(this IQueryable<TSource> query, Expression<Func<TSource, TResult>> propertyAccess) 
    { 
     return query.Select(propertyAccess).Distinct(); 
    } 
} 

public static class StringExtensions 
{ 
    public static Expression<Func<T, TResult>> AsPropertyExpression<T, TResult>(this string propertyName) 
    { 
     var parameter = Expression.Parameter(typeof(T), "x"); 
     var property = typeof(T).GetProperty(propertyName); 
     var body = Expression.MakeMemberAccess(parameter, property); 
     return Expression.Lambda<Func<T, TResult>>(body, parameter); 
    } 
} 

用法:

public class Person 
{ 
    public string Name { get; } 
    public int Age { get; } 

    public Person(string name, int age) 
    { 
     Name = name; 
     Age = age; 
    } 
} 

var people = new Person[] 
{ 
    new Person("John", 25), new Person("Peter", 25), new Person("Sean", 25), 
    new Person("John", 32), new Person("Peter", 32), 
}; 

var query = people.AsQueryable(); 

var namePropertyExpression = "Name".AsPropertyExpression<Person, string>(); 
var agePropertyExpression = "Age".AsPropertyExpression<Person, int>(); 

// When you know the result type 
var names1 = query.GetDistinctValuesForProperty(x => x.Name); 
var ages1 = query.GetDistinctValuesForProperty(x => x.Age); 

// When you know the result type, but you may want to reuse the property expression 
var names2 = query.GetDistinctValuesForProperty(namePropertyExpression); 
var ages2 = query.GetDistinctValuesForProperty(agePropertyExpression); 

// When you just know the property name 
var names3 = query.GetDistinctValuesForProperty("Name"); 
var ages3 = query.GetDistinctValuesForProperty("Age"); 
+0

好,但是如果我没有x.Name。我有列名作为字符串“名称”。 –

+0

你事先知道'TResult'的泛型参数是什么吗? –

+0

不,呼叫从其余的WebService到达,所以我不知道类型的属性。我需要推断里面的功能。我试过personQuery.GetDistinctValuesForProperty(x => x.GetType()。GetProperties()。其中​​(a => a.Name == ColumnName));但不能工作 –

最后我找到了解决方案。我需要包含对System.Linq.Dynamic的引用(由nuget下载),并使用接受字符串引用列的“Select”方法。

using System.Linq.Dynamic; 

public static async Task<IEnumerable<Object>> GetDistinctValuesForProperty<T>(this IQueryable<T> query, String PropertyName) 
    { 
    return await query.Select(PropertyName).Distinct().ToListAsync(); 
    } 

,并调用作为

String ColumnName = "DateTimeInsert"; 
DbSet<Log> oDbSet = _uow.DbContext.Set<Log>(); 

Array DistinctValues; 
if (typeof(Log).GetProperty(ColumnName) != null) 
    { 
    DistinctValues = (await oDbSet.GetDistinctValuesForProperty(ColumnName)).ToArray(); 
    } 
else 
    { 
    DistinctValues = new object[0]; 
    } 

我需要在日期时间类型的情况下,使用阵列VS的IEnumerable由于铸造问题