2016-08-15 72 views
0

如何在最後一次選擇中實現投影?我需要將字符串prop.Name定義的屬性選擇到SeriesProjection對象中。LINQ to SQL通過投影字符串選擇屬性名稱

public override IQueryable<SeriesProjection> FilterOn(string column) 
{ 
    //Get metadata class property by defined Attributes and parameter column 
    var prop = typeof(CommunicationMetaData) 
       .GetProperties() 
       .Single(p => p.GetCustomAttribute<FilterableAttribute>().ReferenceProperty == column); 

    var attr = ((FilterableAttribute)prop.GetCustomAttribute(typeof(FilterableAttribute))); 

    var param = Expression.Parameter(typeof(Communication)); 

    Expression conversion = Expression.Convert(Expression.Property(param, attr.ReferenceProperty), typeof(int)); 

    var condition = Expression.Lambda<Func<Communication, int>>(conversion, param); // for LINQ to SQl/Entities skip Compile() call 

    var result = DbQuery.Include(prop.Name) 
      //.GroupBy(c => c.GetType().GetProperty(attr.ReferenceProperty)) 
      .GroupBy(condition) 
      .OrderByDescending(g => g.Count()) 
      .Select(group => new SeriesProjection() 
      { 
       Count = group.Count(), 
       Id = group.Key, 
       //set this navigation property dynamically 
       Name = group.FirstOrDefault().GetType().GetProperty(prop.Name) 
      }); 

    return result; 
} 

GroupBy我用FK屬性名稱總是在Communication實體int,但對於選擇我想不通的表達。

[編輯]

System.Data.Entity.Infrastructure.DbQuery<Communication> DbQuery; 
--- 
[MetadataType(typeof(CommunicationMetaData))] 
public partial class Communication 
{ 
    public int CommunicationId { get; set; } 
    public Nullable<int> TopicId { get; set; } 
    public int CreateById { get; set; } 
    public virtual Employee CreateByEmployee { get; set; } 
    public virtual Topic Topic { get; set; } 
} 
--- 
public class CommunicationMetaData 
{ 
    [Filterable("By Employee", nameof(Communication.CreateById))] 
    public Employee CreateByEmployee { get; set; } 
    [Filterable("By Topic", nameof(Communication.TopicId))] 
    public Topic Topic { get; set; } 
} 
--- 
[AttributeUsage(AttributeTargets.Property)] 
public class FilterableAttribute : System.Attribute 
{ 

    public FilterableAttribute(string friendlyName, string referenceProperty) 
    { 
     FriendlyName = friendlyName; 
     ReferenceProperty = referenceProperty; 
    } 

    public string FriendlyName { get; set; } 

    public string ReferenceProperty { get; set; } 
} 
--- 
public class SeriesProjection 
{ 
    public int Count { get; set; } 
    public int Id { get; set; } 
    public object Name { get; set; } 
} 
+0

對於更有經驗的人來說,它可能不是必須的,但是你可以同時發佈'SeriesProjection','CommunicationMetaData','FilterableAttribute','Communication'和'DbQuery'類以及'column'輸入以便我可以測試嗎? – uTeisT

+0

您生成了'GroupBy'表達式,爲什麼要在那裏停下來? –

+0

@JeffMercado這正是我的奮鬥,因爲我無法弄清楚。 –

回答

1

如果沒有某種表達助手庫,你必須手動構建整個選擇的表達。

選擇器的輸入將是IGrouping<int, Communication>類型,則結果類型的參數 - SeriesProjection,並且主體將MemberInit表達:

var projectionParameter = Expression.Parameter(typeof(IGrouping<int, Communication>), "group"); 
var projectionType = typeof(SeriesProjection); 
var projectionBody = Expression.MemberInit(
    // new SeriesProjection 
    Expression.New(projectionType), 
    // { 
    //  Count = group.Count(), 
    Expression.Bind(
     projectionType.GetProperty(nameof(SeriesProjection.Count)), 
     Expression.Call(typeof(Enumerable), "Count", new[] { typeof(Communication) }, projectionParameter)), 
    //  Id = group.Key 
    Expression.Bind(
     projectionType.GetProperty(nameof(SeriesProjection.Id)), 
     Expression.Property(projectionParameter, "Key")), 
    //  Name = group.FirstOrDefault().Property 
    Expression.Bind(
     projectionType.GetProperty(nameof(SeriesProjection.Name)), 
     Expression.Property(
      Expression.Call(typeof(Enumerable), "FirstOrDefault", new[] { typeof(Communication) }, projectionParameter), 
      prop.Name)) 
    // } 
    ); 
var projectionSelector = Expression.Lambda<Func<IGrouping<int, Communication>, SeriesProjection>>(projectionBody, projectionParameter); 

然後當然使用簡單:

var result = DbQuery.Include(prop.Name) 
     .GroupBy(condition) 
     .OrderByDescending(g => g.Count()) 
     .Select(projectionSelector);