2016-08-15 5 views
0

Comment puis-je réaliser la projection sur la dernière sélection? J'ai besoin que la propriété définie par la chaîne prop.Name soit sélectionnée dans l'objet SeriesProjection.LINQ to SQL sélectionner le nom de la propriété par chaîne sur la projection

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; 
} 

Pour le GroupBy J'ai utilisé le nom de la propriété fk qui est toujours un int sur l'entité Communication, mais pour la sélection je ne peux pas comprendre l'expression.

[EDIT]

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

Pour les yeux plus expérimentés, cela ne sera probablement pas nécessaire mais pourriez-vous aussi publier les classes 'SeriesProjection',' CommunicationMetaData', 'FilterableAttribute',' Communication' et 'DbQuery' avec l'entrée' column' pour que je puisse tester? – uTeisT

+0

Vous avez généré l'expression 'GroupBy', pourquoi s'arrêter là? –

+0

@JeffMercado c'est exactement mon combat car je ne peux pas le comprendre. –

Répondre

1

Sans une bibliothèque d'aide d'expression, vous devez créer manuellement toute l'expression de sélection.

L'entrée du sélecteur sera un paramètre de type IGrouping<int, Communication>, le type de résultat - SeriesProjection, et le corps sera MemberInit expression:

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); 

puis bien sûr utiliser simplement:

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