2017-12-25 28 views
1

런타임시 저장 프로 시저에서 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이 될 수 있습니다. , 열로 다음과 같은 이미지를 참조하십시오

enter image description here

난 그냥 런타임에서 열 이름과 열의 양에 대해 알고 있습니다.

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

그래서 내 목표는 DataTable에서 속성 클래스를 만드는 것입니다 :

는 런타임에 속성으로 위의 열이있는 클래스를 생성하고 일반적인 방법 같은 이러한 속성에 투사 할 수 있나요 속성이있는 클래스에 DataTable 열을 투영하십시오.

런타임에 속성을 만들고 DataTable 클래스를 새로 만들 수 있습니까?

+2

* 2017-0901_FactPark *, * 2017-10-01_FactPark * ... 누군가가 정상화가 필요 ... – JohnyL

+0

허용 할 수 없거나 허용되지 않습니까? :) – JohnyL

+0

@JohnyL 열 이름은 저장 프로 시저의 SELECT 문에 의해 만들어지며이 SELECT 문을 변경할 수 없습니다. – StepUp

답변

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() 방법. 모든 속성 이름을 검색 할 수 있습니다.

    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"]); 
    
    } 
    

    논리 : 속성 이름을 설정하기 위해 Dot-Property 표기와 바인더 식별자의 이름으로 재산의 이름을 사용하기 때문에 Indexer이 변수를 평가합니다 동안, 당신은, Indexer 표기법을 사용해야에서

앱의 경우 다음과 같습니다. 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 

} 
+0

테스트 할 기회가 없지만 나는 당신을 믿는다 : – StepUp

+1

@StepUp 나는 그것을 철저히 테스트 했으므로 작동한다.) – JohnyL

+0

@JohnyL 좋은데, 나는 두 달 전에 같은 질문에 대해 궁금해했다.하지만 게시하지 않았다. 'ExpandoObjects'를 사용하고 있습니까? – Yollo