Expression.Property(param,field)正在“拖曳”我[System.ArgumentException] = {“没有为类型A定义实例属性'B.Name'”}

时间:2016-10-10 15:28:36

标签: linq exception lambda expression

再一次,我遇到了一个问题,这次是LINQ Expression构建器,这次我甚至在努力找到它无法正常工作的原因。我有一个数据库优先EF项目,有很多表。对于这种特殊情况,我必须使用其中的两个 - DocHead和Contragent。 MyService.metadata.cs如下所示:

[MetadataTypeAttribute(typeof(DocHead.DocHeadMetadata))]
public partial class DocHead
{

    // This class allows you to attach custom attributes to properties
    // of the DocHead class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class DocHeadMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private DocHeadMetadata()
        {
        }

        public string doc_Code { get; set; }
        public string doc_Name { get; set; }
        public string doc_ContrCode { get; set; }
        //...

        [Include]
        public Contragent Contragent { get; set; }
    }
}

[MetadataTypeAttribute(typeof(Contragent.ContragentMetadata))]
public partial class Contragent
{

    // This class allows you to attach custom attributes to properties
    // of the Contragent class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class ContragentMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private ContragentMetadata()
        {
        }

        public string Code { get; set; }
        public string Name { get; set; }
        //...

我带了一些像这样的docHeads:

IQueryable<DocHead> docHeads = new MyEntities().DocHead;

然后我尝试按照这样排序:

docHeads = docHeads.OrderByDescending(x => x.Contragent.Name);

这一切都像我想要的那样工作。我将这些docHeads按加入的Contragent的名称排序。我的问题是我必须按字段对它们进行排序,作为字符串参数给出。我需要能够写出这样的东西:

string field = "Contragent.Name";
string linq = "docHeads = docHeads.OrderByDescending(x => x." + field + ")";
IQueryable<DocHead> result = TheBestLinqLibraryInTheWorld.PrepareLinqQueryable(linq);

不幸的是,TheBestLinqLibraryInTheWorld不存在(暂时)。所以,我已经设置了一种方法作为解决方法。

public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
    {
        var param = Expression.Parameter(typeof(T), "x");
        var prop = Expression.Property(param, SortField); // normally returns x.sortField
        var exp = Expression.Lambda(prop, param); // normally returns x => x.sortField
        string method = Ascending ? "OrderBy" : "OrderByDescending";
        Type[] types = new Type[] { q.ElementType, exp.Body.Type };
        var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp); // normally returns sth similar to q.OrderBy(x => x.sortField)
        return q.Provider.CreateQuery<T>(mce);
    }

通常......是的,当谈到DocHead类的自有属性时 - 那些以doc_为前缀的属性。当我这样称呼这个方法时,灾难来袭了:

docHeads = docHeads.OrderByField<DocHead>("Contragent.Name", true); // true - let it be Ascending order

更具体地说,标题中的异常抛出OrderByField()方法的第2行:

var prop = Expression.Property(param, SortField);

在My.edmx(模型)中,DocHead和Contragent表已经为我设置了关系,如下所示:0..1到*。

再一次,我完全没有问题写“静态”查询。通过OrderByField()方法创建“动态”方法没有问题,但仅限于DocHead类的属性。当我试图通过加入的Contragent类的道具订购时 - 灾难来袭。任何帮助都会非常有用,谢谢!

1 个答案:

答案 0 :(得分:1)

问题是Expression.Property方法不支持嵌套属性。它完全按照它所说的 - 创建表达式,表示由UITableView参数表示的对象的propertyName参数表示的属性。

幸运的是,它很容易扩展。您可以在需要创建嵌套属性访问表达式时随时使用以下简单Split / Aggregate技巧:

expression