C# 创建类的属性并在运行时向其投影
我在运行时从存储过程中的DataTable中获取列。代码如下所示:C# 创建类的属性并在运行时向其投影,c#,entity-framework,linq,select,datatable,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
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。请参见以下带有列的图像:
我可以在运行时知道列名和列的数量
是否可以在运行时创建一个将上述列作为属性的类,并像往常一样将它们投影到这些属性:
public IQueryable<EmployeeDTO> GetEmployees()
{
return db.Employees
.Select(employee => new EmployeeDTO
{
ID = employee.ID,
FirstName = employee.PriceNumber,
LastName = employee.PriceDate
}).AsQueryable();
}
因此,我的目标是创建具有DataTable属性的类,并将DataTable列投影到具有属性的类
是否可以在运行时创建属性,并将DataTable列投影到类的新创建属性 动态构造类形状的一种方法是使用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;
}
}
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
}
应用程序的逻辑如下:您可以从SqlDataReader获取列的名称,并使用它们在动态对象上进行设置
下面是使用SqlDataReader和我们的动态类的一个可能的变体:
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;
}
}
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
}
2017-0901_FactPark,2017-10-01_FactPark。。。有人需要正常化…你不能或不允许@JohnyL列名是由存储过程的SELECT语句创建的,我不允许更改此SELECT语句。我认为您试图将动态列强制转换为静态定义的属性是错误的。为什么不将动态字段公开为键/值集合?这将允许您枚举和查找值,而无需重新编译代码。也就是说,存储过程可以更改,但您的代码不需要更改。您计划如何使用查询结果?当然你不能在代码中使用这个类,因为属性是在运行时生成的,没有机会进行测试,但是我相信你:@JohnyL nice,两个月前我还在想同样的问题,但是没有发布它。这可以用ExpandoObjects完成吗?@Yollo Expando不允许你使用Indexer,也就是说,你不能这样写:exp[Age]=100。错误为:无法将带[]的索引应用于ExpandooObject类型的表达式。