2012-02-06 3 views
3

Я пытаюсь написать некоторый общий linqtoSQL, который выполняет операции над объектами на основе их первичных ключей. Некоторые организации, с которыми я работаю, имеют составные первичные ключи.Использование первичных ключей в linqtoSQL

Фундаментально Я хочу, чтобы иметь возможность делать такие вещи, как:

if(PrimaryKey(foo) == PrimaryKey(bar)) ... 

или,

from oldEntity in oldTableOrCollection 
join newEntity in newTableOrCollection 
on PrimaryKeyOf(oldEntity) equals PrimaryKeyOf(newEntity) 
select new {oldEntity, newEntity} 

только с требованием, чтобы субъекты имеют первичные ключи.

Вот что я нашел до сих пор:

Это можно делать такие вещи, как

var query = from row in oneTableOfRows 
      join otherRow in anotherTableOfRows on 
      new { row.Column1, row.Column2 } 
      equals 
      new { otherRow.Column1, otherRow.Column2} 
      select ...; 

, в котором LinqToSql будет использовать анонимные типы для сравнения (при условии имена свойств совпадают).

я могу затем абстрактный метод для выбора столбцов, на которых можно объединить, чтобы общие типы строк:

void DoQuery<RowType,KeyType>(Table<RowType> oneTableOfRows, 
         Table<RowType> anotherTableOfRows, 
         Func<RowType, KeyType> keySelector) 
{ 
    var query = from row in oneTableOfRow 
      join otherRow in anotherTableOfRows on 
      keySelector(row) 
      equals 
      keySelector(otherRow) 
      select ...; 
} 

... 

Func<rowType, keyType> myKeySelector = row => new { row.Column1, row.Column2 }; 

DoQuery(table, otherTable, myKeySelector); 

То, что я пытаюсь перейти сейчас, когда keySelector будет выбрать первичный ключ любого RowType. Я использую настраиваемый шаблон на основе http://www.codeplex.com/l2st4, чтобы сгенерировать мои объекты (что само по себе довольно похоже на значение по умолчанию в VS). Я надеюсь идеально дать каждому созданному RowType возможность выбрать свой первичный ключ, собранный из dbml. До сих пор ouptut выглядит следующим образом:

public interface IPrimaryKeyEntity<PrimaryKeyType> 
{ 
    PrimaryKeyType PrimaryKey { get; } 
} 

//Here, Product is a table with composite primary key based on its ProductID and it's ProductPriceVersionID columns 

//I've tried using both class and struct as the primary key type for entities 
public class ProductPrimaryKey 
{ 
    public Guid ProductID; 
    public Guid ProductPriceVersionID; 
} 

public partial class Product : IPrimaryKeyEntity<ProductPrimaryKey>, 
           INotifyPropertyChanging, INotifyPropertyChanged 
{ 
    #region IPrimaryKeyEntity Implementation 
public ProductPrimaryKey PrimaryKey 
    { 
     get 
     { return new ProductPrimaryKey() 
        { 
         ProductID = this.ProductID, 
         ProductPriceVersionID = this.ProductPriceVersionID 
        }; 
     } 
    } 
#endregion 

    ... 
    //Rest is mostly standard LinqToSql entity generation 
} 

Возвращаясь к первоначальной цели, теперь я могу скомпилировать следующий для всех моих первичных ключей сущностей:

from oldEntity in oldTableOrCollection 
join newEntity in newTableOrCollection 
on oldEntity.PrimaryKey equals newEntity.PrimaryKey 
select new {oldEntity, newEntity} 

Однако, во время выполнения, я получаю infamous [PrimaryKey] "не имеет поддерживаемого перевода в SQL" исключение.

Я понимаю, что перевести SQL необходимо использовать Expression «ю.ш., однако я довольно знаком с Linq.Expressions и не имели никакого прогресса еще пытается применить его к моей ситуации ...

Если кто-то может улучшить на то, что у меня до сих пор, или есть лучший способ в целом я бы знать ....

Приветствия

+1

Изучение 'Expression', сгенерированного для вашего (рабочего) запроса в отладчике, должно быть хорошим местом для начала. Он находится под «Non-Public members»> «queryExpression». Я тоже новичок в этом, но я скажу, найду ли я что-то :) –

+0

@VladislavZorov: Спасибо за предложение! Я определенно посмотрю, поможет ли это, когда я вернусь в офис завтра. – EdF

ответ

2

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

присоединиться ... на oldEntity. PrimaryKey равно новыйEntity. PrimaryKey

Свойство-get - вызов функции. Linq to SQL не знает эту функцию, поэтому она не может ее перевести.

Единственный способ сделать эту работу, чтобы построить выражение, которое соответствует этому SQL:

 join otherRow in anotherTableOfRows on 
     new { row.Column1, row.Column2 } 
     equals 
     new { otherRow.Column1, otherRow.Column2} 

Там нет другого пути, я могу заверить вас. Вы можете сделать это следующим образом: Во-первых, вы объявляете класс пользовательского кортежа:

class CustomTuple2<T1,T2> 
{ 
public T1 Item1 { get; set; } 
public T2 Item2 { get; set; } 
} 

Вы делаете это для всех возможных графов членов (например, 8 или 16).

Давайте посмотрим на то, как присоединиться переводится:

IQueryable<T1> t1 = ...; //table 1 
IQueryable<T2> t2 = ...; //table 2 
var joined = t1.Join(t2, _t1 => _t1.Key, _t2 => _t2.Key); 

Параметры лямбда являются выражениями. Вам нужно построить эти два выражения, используя деревья выражений. Это волшебство!

Например, первая лямбда:

var param = Expression.Parameter(typeof(T1), "_t1"); 
var key = Expression.Property(param, "Key"); 
var lambda1 = Expression.Lambda(param, key); 

Сделайте то же самое для второго лямбды. (Помните, что это псевдо-код, который я точно не помню, как назывались все.).

Наконец, вызовите инф:

var joined = typeof(Queryable).GetMethod("Join").Invoke(t1, t2, lambda1, lambda2); 

Так что, как вы строите объединения во время выполнения! Обратите внимание, что все это был псевдокод. Это потребует от вас некоторых исследований, чтобы понять API и заставить его работать, но вы можете это сделать.

Кроме того, если у вас есть составной ключ, вам понадобится упомянутый класс кортежей для проецирования ключевых членов.

+0

Спасибо, выглядит многообещающе. Любая идея, будет ли подход работать с явно определенными 'FooKey' структурами/классами, которые я сейчас генерирую, а не кортежей? Кроме того, есть ли хороший способ спрятать дерево выражений с этим свойством, чтобы linqtosql использовал тот же интерфейс, что и обычный доступ к ресурсам среды выполнения? что-то вроде наличия свойства, которое либо полностью оценивает, либо возвращает дерево выражений в зависимости от того, что необходимо? – EdF

+0

a) Он также будет работать с явными классами. Важно то, что ваш класс DTO имеет по умолчанию ctor и присваиваемые авто-свойства. б) Я знаю, что вы имеете в виду. Это очень сложно. Вы хотите иметь «вычисленные столбцы», которые работают в L2S и L2Objects. Вероятно, лучший способ сделать это - поставить фасад перед реализацией IQueryable datacontext и переписать дерево выражений, прежде чем он попадет на L2S. Но это, вероятно, не подходит для вас. – usr

Смежные вопросы