我正在尝试编写一些通用的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}
但是,运行时,我得到臭名昭着的[PrimaryKey]“没有支持的转换为SQL”异常。
我理解为了翻译成SQL,有必要使用Expression
,但是我对Linq.Expressions
非常不熟悉,并且还没有尝试将其应用到我的情况...... < / p>
如果有人能够改进到目前为止所取得的成绩,或者总体上有更好的方法,我会很高兴知道....
干杯
答案 0 :(得分:2)
问题是您正在调用函数此处:
加入... on oldEntity。 PrimaryKey 等于newEntity。 PrimaryKey
property-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);
lambda参数是表达式。 您需要使用表达式树构建这两个表达式。这就是魔术!
例如,第一个lambda:
var param = Expression.Parameter(typeof(T1), "_t1");
var key = Expression.Property(param, "Key");
var lambda1 = Expression.Lambda(param, key);
对第二个lambda做同样的事情。 (请记住,这是伪代码。我并不完全记得如何调用所有内容。)
最后,调用Join:
var joined = typeof(Queryable).GetMethod("Join").Invoke(t1, t2, lambda1, lambda2);
这就是你如何在运行时构建连接!请注意,所有这些都是伪代码。它将带你进行一些研究,以确定API并使其工作,但你一定可以做到。
另外,如果你有一个复合键,你需要提到的元组类来将关键成员投射到。