Динамический поворот в LINQ: поворотные столбцы, определенные во время выполнения

1

Скажем, у меня три таблицы. В принципе, одна таблица вещей, одна таблица, описывающая возможные атрибуты вещей, и таблица моста, предоставляющая значения для этих атрибутов для конкретной вещи. Это позволяет выполнять динамические метаданные.

Пользователи могут добавлять свойства метаданных в любое время, а затем они могут предоставлять значения для этих свойств для любой вещи в таблице "вещи".

Ну вот так:

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, прежде чем публиковать этот вопрос, и если этот точный вопрос был задан раньше, я не смог его найти.)

Теги:
linq

2 ответа

2
Лучший ответ

Динамические выражения можно выражать динамически. Этот раздел рассматривается (включая пример) в следующей статье MSDN: http://msdn.microsoft.com/en-us/library/bb882637.aspx

Мое предложение было бы написать пример запроса Linq для вашей задачи и программно перестроить его с помощью деревьев выражений. Как только он работает, вы адаптируете его и вводите свои динамические части.

  • 0
    В этом случае мне все еще нужно знать, как развернуть один столбец в существующем запросе. Является ли GroupJoin () лучшим способом сделать это?
  • 0
    Да, вы можете использовать GroupJoin, чтобы выбрать Person и список его свойств.
0

Я думаю, что вы можете присоединиться к условию where следующим образом:

Персональный класс

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
Bob       |   Birthday        |   
Fred      |   SupervisorName  |         
Fred      |   Age             |   35
Fred      |   Birthday        |   5/5/1978
Sally     |   SupervisorName  |         
Sally     |   Age             |
Sally     |   Birthday        |   4/4/1937 
  • 0
    Что, если я хочу сделать Имя, День рождения, EmpId сегодня, а завтра я хочу сделать Имя, Возраст, Супервайзер? Где День рождения, EmpId, Возраст и Супервайзер - все это разные Свойства. Я не думаю, что этот запрос может объяснить это.
  • 0
    Я полагаю, что вы могли бы написать расширение linq для этого. Это всего лишь простой случай.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню