2014-05-15 23 views
1

假設我有三個表格。基本上,一張桌子上的東西,一張描述事物可能屬性的表格,以及一張爲特定事物提供這些屬性值的橋樑表格。這可以讓你做動態元數據。在LINQ中動態旋轉:在運行時確定的旋轉列

用戶可以隨時添加元數據屬性,然後他們可以爲「事物」表中的任何事物提供這些屬性的值。

所以這樣的:

Table: Persons 
PersonID | FirstName | LastName 
1  | Bob  | Jones 
2  | Fred  | Smith 
3  | Sally  | Doe 

Table: Properties 
PropertyID | Name 
1   | SupervisorName 
2   | Age 
3   | Birthday 
4   | EmployeeNumber 
5   | Hometown 

Table: PropertyValues 
PersonID | PropertyID | PropertyValue 
1  | 1   | Frank Grimes 
1  | 2   | 47 
2  | 2   | 35 
2  | 4   | 1983738 
2  | 3   | 5/5/1978 
3  | 3   | 4/4/1937 
3  | 5   | Chicago, IL 

因此,用戶希望能夠查看這些屬性的報告。也許我想看一張包含所有員工年齡和生日的表格。如果這些值沒有填充給這些用戶,表中會有空白。那麼也許我想看看一個包含主管,年齡和生日的報告 - 我應該可以隨時生成該表格。

現在,爲了做到這一點與SQL,我會動態構造一個查詢,並添加一個連接到該查詢爲每個屬性,我想旋轉到頂部。這就是現在的工作原理。

如果我想在LINQ中這樣做,並且我知道在編寫代碼時需要支持哪些屬性,我也可以這樣做 - 我只是使用GroupJoin()。

我不知道的是如何動態地構建一個LINQ查詢,這將允許我在運行時轉換任意數量的屬性,而不必知道它們是否提前。

任何想法?

(之前,你膝蓋反射標記爲重複,知道我發佈此問題之前做了相當數量的StackOverflow研究,並且如果此確切問題以前已詢問,我找不到它。)

+0

http://stackoverflow.com/questions/5675336/linq-pivot-with-dynamic-columns?rq=1可能的副本,但沒有問題的好答案。 – Guvante

+0

看起來我應該可以在這裏做一些修飾器模式,在這裏我運行要添加的屬性列表,並動態地添加一些內容到我現有的查詢中,以追加一列。你不這麼認爲? – SuperNES

回答

2

您可以動態地構建linq表達式樹。在下面的MSDN文章中涵蓋了此主題(包括示例):http://msdn.microsoft.com/en-us/library/bb882637.aspx

我的建議是爲您的任務編寫一個示例Linq查詢並使用表達式樹以編程方式重建它。一旦它起作用,就可以調整它並注入動態部分。

+0

在這種情況下,我仍然需要知道如何將一列轉換爲現有的查詢。 GroupJoin()是最好的方法嗎? – SuperNES

+0

是的,您可以使用GroupJoin來選擇Person和其屬性列表。 – Edin

0

我的想法是,你可以用where條件如下連接:

Person類

public class Person 
{ 
    public int Id {get; set;} 
    public string FirstName {get; set;} 
    public string LastName {get; set;} 
} 

類財產

public class Property 
{ 
    public int Id {get; set;} 
    public string Name {get; set;} 
} 

值類

public class Value 
{ 
    public int PersonId {get; set;} 
    public int PropertyId {get; set;} 
    public string Val {get; set;} 
} 

代碼

void Main() 
{ 
    var selectBy = "Birthday"; 

    var persons = new List<Person>() { new Person {Id = 1, FirstName = "Bob", LastName = "Jones"}, new Person {Id = 2, FirstName = "Fred", LastName = "Smith"}, new Person {Id = 3,FirstName = "Sally", LastName = "Doe"}}; 
    var properties = new List<Property>() { new Property {Id = 1, Name = "SupervisorName"}, new Property {Id = 2, Name = "Age"}, new Property {Id = 3, Name = "Birthday"}, new Property {Id = 4, Name = "EmployeeNumber"}, new Property {Id = 5, Name = "Hometown"}}; 
    var values = new List<Value>() { new Value {PersonId = 1, PropertyId = 1, Val="Frank Grimes"}, new Value {PersonId = 1, PropertyId = 2, Val="47"}, new Value {PersonId = 2, PropertyId = 2, Val="35"}, new Value {PersonId = 2, PropertyId = 4, Val="1983738"}, new Value {PersonId = 2, PropertyId = 3, Val="5/5/1978"}, new Value {PersonId = 3, PropertyId = 3, Val="4/4/1937"}, new Value {PersonId = 3, PropertyId = 5, Val="Chicago, IL"}}; 


    var result = from v in values 
       join p in persons on v.PersonId equals p.Id 
       join p2 in properties on v.PropertyId equals p2.Id 
       where p2.Name.Equals(selectBy) 
       select new { Name = p.FirstName + " " + p.LastName, 
          Value = v.Val 
          }; 

    result.Dump(); 
} 

結果

名稱,價值

Fred Smith 5/5/1978 
Sally Doe 4/4/1937 

修訂答

void Main() 
{ 
    var selectBy = "Birthday"; 

    var persons = new List<Person>() { new Person {Id = 1, FirstName = "Bob", LastName = "Jones"}, new Person {Id = 2, FirstName = "Fred", LastName = "Smith"}, new Person {Id = 3,FirstName = "Sally", LastName = "Doe"}}; 
    var properties = new List<Property>() { new Property {Id = 1, Name = "SupervisorName"}, new Property {Id = 2, Name = "Age"}, new Property {Id = 3, Name = "Birthday"}, new Property {Id = 4, Name = "EmployeeNumber"}, new Property {Id = 5, Name = "Hometown"}}; 
    var values = new List<Value>() { new Value {PersonId = 1, PropertyId = 1, Val="Frank Grimes"}, new Value {PersonId = 1, PropertyId = 2, Val="47"}, new Value {PersonId = 2, PropertyId = 2, Val="35"}, new Value {PersonId = 2, PropertyId = 4, Val="1983738"}, new Value {PersonId = 2, PropertyId = 3, Val="5/5/1978"}, new Value {PersonId = 3, PropertyId = 3, Val="4/4/1937"}, new Value {PersonId = 3, PropertyId = 5, Val="Chicago, IL"}}; 


    // Default Values for the Cartesian Product 
    var defaultValues = new string[]{"","","","",""}; 

    // propertyKeys are used to filter values generated for pivot table 
    var propertyKeys = new List<Property> { new Property{Id=1}, new Property{Id=2}, new Property{Id=3}}; 

    // Generate default values for every person and each property 
    var cartesianProduct = from ppl in persons 
          from prop in properties 
          join pk in propertyKeys on prop.Id equals pk.Id 
          select new {PersonId = ppl.Id, PropertyId = prop.Id, Val = defaultValues[prop.Id-1]}; 

    // Create Pivot Values based on selected PropertyIds 
    var newValues = from cp in cartesianProduct 
        join v in values on new {cp.PersonId, cp.PropertyId} equals new { v.PersonId, v.PropertyId } into gj 
        from x in gj.DefaultIfEmpty() 
        select new { 
         PersonId = (x == null ? cp.PersonId : x.PersonId), 
         PropertyId = (x == null ? cp.PropertyId: x.PropertyId), 
         Val = (x == null ? cp.Val : x.Val) 
        }; 


    foreach(var y in newValues) 
    { 
     var aPerson = persons.Where(r=> r.Id == y.PersonId).First().FirstName; 
     var aProperty = properties.Where(r=> r.Id == y.PropertyId).First().Name; 
     Console.WriteLine(string.Format("{0:12}   {1:12}   {2:12}", aPerson, aProperty, y.Val)); 
    } 
} 

結果:
鮑勃SupervisorName弗蘭克·格賴姆斯
鮑勃年齡47
鮑勃生日
佛瑞德SupervisorName
佛瑞德年齡35
佛瑞德生日1978年5月5日
薩利SupervisorName
莎莉年齡
莎莉生日4/4/1937

+0

如果我想今天做名字,生日,EmpId,明天我想做名字,年齡,主管,該怎麼辦?生日,EmpId,年齡和主管都屬於不同的屬性。我不認爲這個問題可以解釋這一點。 – SuperNES

+0

我相信你可以爲此寫一個linq擴展。這只是一個簡單的例子。 – n4gy3

+0

我必須想出如何做到最好,我會分享。 – n4gy3