在运行时为它们创建类和项目的属性

时间:2017-12-25 07:29:52

标签: c# entity-framework linq select datatable

我从存储过程中获取运行时DataTable的列。代码如下所示:

var parkDataTable = new DataTable("tmp");
...
SqlCommand cmd = new SqlCommand("FooStoredProcedure", db.Database.Connection as 
    SqlConnection, transaction.UnderlyingTransaction as SqlTransaction);
cmd.CommandType = CommandType.StoredProcedure;    
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
parkDataTable.Load(dr);

有趣的是,我的列可以通过向列名添加日期来使用各种名称。例如,我有一个列名2017-09-01_FactPark,然后下一次可以是2017-10-01_FactPark。请参阅以下图片,其中包含以下列:

https://docs.python.org/3.6/library/re.html#re.findall

我可以在运行时了解列名和列数。

是否可以在运行时创建一个具有上述列作为属性的类,并像往常一样将它们投影到这些属性:

public IQueryable<EmployeeDTO> GetEmployees()
{
    return db.Employees
        .Select(employee => new EmployeeDTO
        {
            ID = employee.ID,                    
            FirstName = employee.PriceNumber,
            LastName = employee.PriceDate                                        
        }).AsQueryable();
}

所以我的目标是创建具有DataTable和项目DataTable列的属性的类到具有属性的类。

是否可以在运行时创建属性并将DataTable列投影到新创建的类属性?

1 个答案:

答案 0 :(得分:3)

动态构成班级形状的一种方法是使用DynamicObject类。您可以创建自己的动态类,使用它们的值来设置和获取属性。

现在,让我们实现我们的动态类:

using System.Dynamic;

class Dynamo : DynamicObject
{
    private Dictionary<string, object> items = new Dictionary<string, object>();

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        return items.TryGetValue(binder.Name, out result);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        items[binder.Name] = value;
        return true;
    }

    public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
    {
        string propName = indexes[0] as string;
        items[propName] = value;
        return true;
    }

    public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
    {
        string propName = indexes[0] as string;
        return items.TryGetValue(propName, out result);
    }

    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return items.Keys;
    }
}

我们在这里:

  1. items字典。允许我们存储任何值并按名称获取。
  2. TryGetMember()/TrySetMember()方法。该群组允许我们使用Dot-Property表示法设置/获取值:Class.Property
  3. TrySetIndex()/TryGetIndex()方法。该群组允许我们使用Indexer表示法设置/获取值:Class[varHoldingPropName]Class["propName"]
  4. GetDynamicMemberNames()方法。允许检索所有属性名称。
  5. 要设置属性名称,您必须使用Indexer表示法,因为使用Dot-Property表示法时,活页夹将使用您的属性名称作为标识符名称,而Indexer将评估您的变量:

    static void UseDynamicObject()
    {
    
        var colorProperty = "Color";
        var ageProperty = "Age";
    
        dynamic dynamo = new Dynamo();
        dynamo.colorProperty = "red";
        dynamo[ageProperty] = 20;
    
        // DOT-PROPERTY NOTATION
        // Error: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
        // 'Dynamo' does not contain a definition for 'Color'.
        Console.WriteLine(dynamo.Color);
        // The property used to retrieve value "red" is "colorProperty" rather "Color".
        Console.WriteLine(dynamo.colorProperty);
    
        // INDEXER NOTATION
        // We can use either variable or literal name of the property,
        // so these two lines are equivalent.
        Console.WriteLine(dynamo[ageProperty]);
        Console.WriteLine(dynamo["Age"]);
    
    }
    

    应用程序的逻辑如下:您可以获取列&#39;来自SqlDataReader的名称,并使用它们在动态对象上设置它们。

    这是使用SqlDataReader和动态类的一种可能变体:

    static void Main(string[] args)
    {
    
        var data = new List<dynamic>();
        List<string> cols = default;
    
        using (var conn = new SqlConnection(connStr))
        {
            conn.Open();
            using (var comm = new SqlCommand("SELECT * FROM dbo.Client", conn))
            {
                using (var reader = comm.ExecuteReader())
                {
                    // Get columns names
                    cols = Enumerable.Range(0, reader.FieldCount)
                           .Select(i => reader.GetName(i)).ToList();
                    while (reader.Read())
                    {
                        dynamic obj = new Dynamo();
                        cols.ForEach(c => obj[c] = reader[c]);
                        data.Add(obj);
                    }
                }
            }
        }
    
    
        /* OUTPUT DATA */
    
        // To get columns names, you can:
        // 1) use ready "cols" variable
        // 2) use "GetDynamicMemberNames()" on first object:
        //    IEnumerable<string> cols = data[0].GetDynamicMemberNames();
        foreach (var obj in data)
        {
            Console.WriteLine(String.Join(", ", cols.Select(c => $"{c} = {obj[c]}")));
        }
    
        // Output:
        // ClientId = 1, ClientName = Client1
        // ClientId = 2, ClientName = Client2
    
    }