在LINQ中动态转动:在运行时确定的枢轴列

时间:2014-05-15 19:50:56

标签: c# sql 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研究,如果之前已经问过这个问题,我找不到它。)

2 个答案:

答案 0 :(得分:2)

您可以动态构建linq表达式树。以下MSDN文章中介绍了此主题(包括示例):http://msdn.microsoft.com/en-us/library/bb882637.aspx

我的建议是为您的任务编写一个示例Linq查询,并使用表达式树以编程方式重建它。一旦它工作,你就可以调整它并注入动态部件。

答案 1 :(得分:0)

我的想法是你可以加入以下条件:

人员类

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

<强>结果:
Bob SupervisorName Frank Grimes
Bob Age 47
鲍勃生日
Fred SupervisorName
Fred Age 35
弗雷德生日5/5/1978
莎莉主管姓名
莎莉年龄
莎莉生日4/4/1937