Скажем, у меня три таблицы. В принципе, одна таблица вещей, одна таблица, описывающая возможные атрибуты вещей, и таблица моста, предоставляющая значения для этих атрибутов для конкретной вещи. Это позволяет выполнять динамические метаданные.
Пользователи могут добавлять свойства метаданных в любое время, а затем они могут предоставлять значения для этих свойств для любой вещи в таблице "вещи".
Ну вот так:
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, прежде чем публиковать этот вопрос, и если этот точный вопрос был задан раньше, я не смог его найти.)
Динамические выражения можно выражать динамически. Этот раздел рассматривается (включая пример) в следующей статье MSDN: http://msdn.microsoft.com/en-us/library/bb882637.aspx
Мое предложение было бы написать пример запроса Linq для вашей задачи и программно перестроить его с помощью деревьев выражений. Как только он работает, вы адаптируете его и вводите свои динамические части.
Я думаю, что вы можете присоединиться к условию 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