从字符串构建动态LINQ查询 - 使用Reflection?

时间:2016-11-29 11:54:15

标签: entity-framework linq generics

我有一些单词模板(可能有数千个)。每个模板都有合并字段,这些字段将从数据库中填充。我不喜欢为每个模板编写单独的代码,然后构建应用程序并在模板更改或添加模板上的字段时进行部署!

相反,我试图在一个单独的xml文件中定义所有合并字段,并且对于我想要编写"查询的每个字段"将在需要时调用。 EX:

  1. mergefield1将调用查询" Case.Parties.FirstOrDefault.NameEn"
  2. mergefield2将调用查询" Case.CaseNumber"
  3. mergefield3将调用查询" Case.Documents.FirstOrDefault.DocumentContent.DocumentType"
  4. 所以,对于一个特定的模板,我扫描它的合并字段,对于每个合并字段,我把它作为"查询定义"并使用EntityFramework和LINQ将该请求发送到数据库。防爆。它适用于这些查询:"" TimeSlots.FirstOrDefault.StartDateTime"要么 " Case.CaseNumber"

    这将是一个引擎,它将生成word文档并用xml中的合并字段填充它。此外,它适用于任何新模板或新合并字段。

    现在,我使用反射工作了一个版本。

    public string GetColumnValueByObjectByName(Expression<Func<TEntity, bool>> filter = null, string objectName = "", string dllName = "", string objectID = "", string propertyName = "")
        {
            string objectDllName = objectName + ", " + dllName;
            Type type = Type.GetType(objectDllName);
            Guid oID = new Guid(objectID);
            dynamic Entity = context.Set(type).Find(oID); // get Object by Type and ObjectID
    
            string value = ""; //the value which will be filled with data from database
    
            IEnumerable<string> linqMethods = typeof(System.Linq.Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).Select(s => s.Name).ToList(); //get all linq methods and save them as list of strings
    
            if (propertyName.Contains('.'))
            {
                string[] properies = propertyName.Split('.');
                dynamic object1 = Entity;
                IEnumerable<dynamic> Child = new List<dynamic>();
                for (int i = 0; i < properies.Length; i++)
                {
                    if (i < properies.Length - 1 && linqMethods.Contains(properies[i + 1]))
                    {
                        Child = type.GetProperty(properies[i]).GetValue(object1, null);
                    }
                    else if (linqMethods.Contains(properies[i]))
                    {
                        object1 = Child.Cast<object>().FirstOrDefault(); //for now works only with FirstOrDefault - Later it will be changed to work with ToList or other linq methods
                        type = object1.GetType();
                    }
                    else
                    {
                        if (linqMethods.Contains(properies[i]))
                        {
                            object1 = type.GetProperty(properies[i + 1]).GetValue(object1, null);
                        }
                        else
                        {
                            object1 = type.GetProperty(properies[i]).GetValue(object1, null);
                        }
                        type = object1.GetType();
                    }
                }
    
                value = object1.ToString(); //.StartDateTime.ToString();
            }
    
            return value;
        }
    

    我不确定这是否是最佳方法。有没有人有更好的建议,或者有人已经做过这样的事情?

    缩短它:想法是通过以下字符串对数据库进行泛型linq查询:&#34; Case.Parties.FirstOrDefault.NameEn&#34;。

1 个答案:

答案 0 :(得分:0)

你的方法非常好。我毫不怀疑它已经有效了。

另一种方法是使用像@Egorikas建议的表达式树。

免责声明:我是该项目的所有者Eval-Expression.NET

简而言之,这个库允许您在运行时评估几乎所有C#代码(您真正想要做什么)。

我建议你改用我的库。保留代码:

  • 更易阅读
  • 更容易支持
  • 增加一些灵活性

实施例

public string GetColumnValueByObjectByName(Expression<Func<TEntity, bool>> filter = null, string objectName = "", string dllName = "", string objectID = "", string propertyName = "")
{
    string objectDllName = objectName + ", " + dllName;
    Type type = Type.GetType(objectDllName);
    Guid oID = new Guid(objectID);
    object Entity = context.Set(type).Find(oID); // get Object by Type and ObjectID

    var value = Eval.Execute("x." + propertyName, new { x = entity });
    return value.ToString();
}

该库还允许您使用带有IQueryable的动态字符串

维基:LINQ-Dynamic