【问题标题】:Insert Func<T, int> value into linq-2-sql query将 Func<T, int> 值插入 linq-2-sql 查询
【发布时间】:2012-02-13 10:50:19
【问题描述】:

我有一个多租户应用程序,所有数据都在同一个数据库中。我们通过 objectToTenant 表拆分每个租户的数据。

在通用 Repository 类中,我想实现一个过滤对象的方法,只需指定对象的哪个属性是 tenantToObjectRelationId 以及对象是什么类型(需要,因为不同的类型存储在关系表中)

我想拨打的电话应该是这样的:

// Specialized Repository class 
var allItemsForTenant =
    this.AllForTenant(item => item.RelationId, 123);

因此我实现了这个方法:

// Generic Repository class
public virtual IQueryable<T> AllForTenant(
    Func<T, int> getObjectToTenantRelationId, string objectType)
{
    var result =
        from item in this.context.GetTable<T>( )
        join tenantObject in (
            from tenantRelationRecord in objectTenantRelationRepo.All( )
            where tenantRelationRecord.TenantId == Global.TenantId
            where tenantRelationRecord.ObjectTypeId == type.TypeId
            select tenantObjectRecord)
            on getObjectToTenantRelationId( item ) equals tenantObject.ObjectId
        select item;

    return result;
}

但此刻我收到了NotSupportedException

Für die System.Object DynamicInvoke(System.Object[])-Methode gibt es keine unterstützte Übersetzung 在 SQL 中。

Func&lt;T, int&gt;方法无法翻译成sql造成的。

我需要如何更改Func&lt;T, int&gt; 以便它可以被翻译或有人提供其他解决方案?

【问题讨论】:

  • 而不是传入Func&lt;T, int&gt; 传入IQueryable 与所有可能的项目并使用它来加入。
  • 要使实体框架能够翻译它,您必须传入一个Expression&lt;Func&lt;T, int&gt;&gt;

标签: c# .net linq-to-sql generics c#-4.0


【解决方案1】:

最简单的解决方法是传递Expression&lt;Func&lt;T,int&gt;&gt; 而不仅仅是Func&lt;T,int&gt;。传递一个表达式将允许树访问者“进入”您的表达式,而不是弹回它的名字。

很好的是,如果您传递 lambda,编译器会自动从中创建 Expression&lt;Func&lt;T,int&gt;&gt;,因此您唯一需要更改的是您的 AllForTenant 方法的签名。

【讨论】:

  • 如果我将签名更改为Expression&lt;Func&lt;T,int&gt;&gt;,我会收到错误'getObjectToTenantRelationId' is a 'variable' but is used like a method
  • 仅当 LINQ to SQL 可以将其转换为 SQL 时,使用 Expression 才有效。
  • @Viper:虽然我不知道如何使用查询式语法应用Expression&lt;Func&lt;T,int&gt;&gt;,但是如果您查看扩展方法语法,那么.Join() 接受表达式。我已经多次使用它进行过滤,情况相同 - 您必须切换到 .Where() 而不是 where
  • 我不知道到底有什么区别,但是从查询样式语法更改为扩展方法 syntay 就可以了。 this.All( ).Join( tenantObjects, objectToTenantRelationsIds, item =&gt; item.ObjectId, (a, b) =&gt; a )
  • @Viper:区别在于扩展方法语法更强大——它同时支持Func&lt;&gt;Expression&lt;Func&lt;&gt;&gt;
【解决方案2】:

而不是传递 Func&lt;T, int&gt; 传递 IQueryable&lt;Tuple&lt;T, int&gt;&gt; 或类似的东西。这样 LINQ to SQL 就知道如何将其转换为 SQL:

// Generic Repository class
public virtual IQueryable<T> AllForTenant(
    IQueryable<Tuple<T, int>> objectToTenentRelationIds, string objectType)
{
    var tenantObjects =
        from tenantRelationRecord in objectTenantRelationRepo.All()
        where tenantRelationRecord.TenantId == Global.TenantId
        where tenantRelationRecord.ObjectTypeId == type.TypeId
        select tenantObjectRecord;

    var result =
        from item in this.context.GetTable<T>()
        join objectToTenentRelationId in objectToTenentRelationIds
            on objectToTenentRelationId.Item1 equals item
        join tenantObject in tenantObjects
            on objectToTenentRelationIds.Item2 equals tenantObject.ObjectId
        select item;

    return result;
}

【讨论】:

  • 如果我按照您的建议进行操作,那么我需要在调用通用基础存储库的方法之前选择所有对象并将它们作为参数传递。这是我在实施过程中尽量避免的事情。
  • 不必从数据库中获取所有内容。不要忘记IQueryable 只是查询的定义,而不是数据本身。 LINQ to SQL 会将其转换为高效的 SQL 查询。
  • 抱歉,可能我表达的不够清楚。我的意思是,当我想执行您的查询时,我需要在专用存储库中进行额外的 linq-2-sql 查询,以便能够将 IQueryable 传递到 BaseRepository 方法。如果我需要这样做,那么我可以直接在专用存储库中加入 objectTenantRelation。
  • 您不能在 LINQ 查询中使用 .NET 方法,因为 LINQ to SQL 无法将其转换为 SQL。你必须传递一些可以翻译的东西。 IQueryable 可以。没有太多的选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-04
  • 1970-01-01
  • 2013-11-06
  • 1970-01-01
  • 1970-01-01
  • 2011-11-07
相关资源
最近更新 更多