简体   繁体   English

在linqtoSQL中使用主键

[英]Using primary keys in linqtoSQL

I am trying to write some generic linqtoSQL which performs operation on entities based on their primary keys. 我正在尝试编写一些通用的linqtoSQL,它基于主键对实体执行操作。 Some entities I am working with have composite primary keys. 我正在使用的一些实体具有复合主键。

Fundamentally I want to be able to do things such as: 从根本上说,我希望能够做到以下事情:

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

or, 要么,

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

with only the requirement that the entities have primary keys. 仅要求实体具有主键。

Here's what I've found so far: 这是我到目前为止所发现的:

It's possible to do things such as 有可能做的事情如

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

in which linqtosql will use the anonymous types for comparison (provided the property names match). 其中linqtosql将使用匿名类型进行比较(假设属性名称匹配)。

I can then abstract the method for selecting the columns on which to join to allow generic row types: 然后,我可以抽象选择要连接的列的方法,以允许通用行类型:

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

What I'm trying to move to now, is where the keySelector will select the primary key of any RowType. 我现在想要的是,keySelector将选择任何RowType的主键。 I am using a custom template based on http://www.codeplex.com/l2st4 to generate my entities (which itself is fairly similar to the default in VS). 我正在使用基于http://www.codeplex.com/l2st4的自定义模板来生成我的实体(它本身与VS中的默认值非常相似)。 I'm hoping to ideally give each generated RowType the ability to select its primary key as gathered from the dbml. 我希望理想情况下让每个生成的RowType能够选择从dbml收集的主键。 So far the ouptut looks like the following: 到目前为止,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
}

Going back to the original aim, I can now compile the following for all my primary key entities: 回到最初的目标,我现在可以为所有主键实体编译以下内容:

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

However, runtime, I get the infamous [PrimaryKey] "has no supported translation to SQL" exception. 但是,运行时,我得到臭名昭着的[Pri​​maryKey]“没有支持的转换为SQL”异常。

I understand that to translate to SQL it is necessary to use Expression 's however I am pretty unfamiliar with Linq.Expressions and have had no progress yet trying to apply it to my situation... 我明白要转换为SQL,有必要使用Expression ,但是我对Linq.Expressions熟悉,并且还没有进展,试图将它应用到我的情况......

If anyone can improve on what I've got so far or has a better method in general I'd be grateful to know.... 如果有人能够改进我迄今为止所拥有的东西或者总体上有更好的方法我会很高兴知道....

Cheers 干杯

The problem is that you are calling a function here : 问题是你在这里调用一个函数:

join ... on oldEntity. 加入... on oldEntity。 PrimaryKey equals newEntity. PrimaryKey等于newEntity。 PrimaryKey 首要的关键

A property-get is a function call. property-get是一个函数调用。 Linq to SQL does not know this function so it cannot translate it. Linq to SQL不知道这个函数所以它无法翻译它。

The only way to make this work is to build an expression that corresponds to this SQL: 使这项工作的唯一方法是构建一个与此SQL对应的表达式:

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

There is no other way, I can assure you. 没有其他办法,我可以向你保证。 You can do it like this: First, you declare a custom tuple class: 你可以这样做:首先,你声明一个自定义元组类:

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

You do this for all possible member counts (for example 8 or 16). 您对所有可能的成员计数执行此操作(例如8或16)。

Let's look at how a join gets translated: 我们来看看如何翻译联接:

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

The lambda parameters are expressions. lambda参数是表达式。 You need to build these two expressions using expression trees. 您需要使用表达式树构建这两个表达式。 This is the magic! 这是神奇的!

For example, the first lambda: 例如,第一个lambda:

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

Do the same for the second lambda. 为第二个lambda做同样的事情。 (Remember, that this was pseudo-code. I don't exactly recall how everything was called.) (请记住,这是伪代码。我并不完全记得所有内容的调用方式。)

Finally, invoke the Join: 最后,调用Join:

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

So that is how you build a join at runtime! 这就是你在运行时构建连接的方法! Please note, that all of this was pseudo-code. 请注意,所有这些都是伪代码。 It will take you some research to figure the APIs out and make it work, but you can surely do it. 它会带你进行一些研究来确定API并使其工作,但你一定可以做到。

Also, if you have a composite key, you will need the mentioned tuple class to project the key members into. 此外,如果您有一个复合键,则需要提到的元组类来将关键成员投影到其中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM