LINQ to SQL sélectionne le nom de la propriété par chaîne sur la projection

c# expression-trees linq

Question

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 .

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 GroupBy j'ai utilisé le nom de la propriété fk qui est toujours un entier sur l'entité Communication , mais je ne peux pas comprendre l'expression avec la sélection.

[MODIFIER]

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

Réponse acceptée

Sans une bibliothèque d'aide aux expressions, vous devez créer manuellement toute l'expression du sélecteur.

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

et bien sûr, utilisez simplement:

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


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi