我有兩個運算式用于我的物體專案的全域查詢過濾器。
我有以下編譯時間之一:
Expression<Func<RandomTable, bool>> test = c => AuthorisedUsers.Any(b => b.RandomTableId == c.RandomTableId && b.UserId == UserId);
AuthorisedUsers 是一個串列,其中包含一個簡單的 userid 和 randomtableid 結構,以確保訪問 RandomTable 的人已在 authorisedusers 表中獲得授權,該表是一個 1-m 表。一個用戶可以訪問許多 RandomTables
上面的運算式創建了以下完美的 SQL
SELECT *
FROM [RandomTable] AS [h]
WHERE EXISTS (
SELECT 1
FROM [AuthorisedUsers] AS [p0]
WHERE ([p].[ID] = [p0].[RandomTableId]) AND ([p0].[UserId] = @__userId_0))
但是后來我在運行時使用 lambda 運算式創建了相同的運算式,但針對我的 dbcontext 中的每個表。然而,相同的運算式然后創建了這個 sql:
SELECT *
FROM [RandomTable] AS [h]
WHERE EXISTS (
SELECT 1 <-- Why are we querying this to then query it again below?
FROM [AuthorisedUsers] AS [p] <--
WHERE EXISTS (
SELECT 1
FROM [AuthorisedUsers] AS [p0]
WHERE ([p0].[RandomTableId] = [p].[RandomTableId]) AND ([p0].[UserId] = @__ef_filter__GetUserId_1)) AND (([p].[RandomTableId] = [h].[RandomTableId]) AND ([p].[UserId] = @__ef_filter__GetUserId_0)))
正如我們在上面的查詢中看到的,它似乎毫無意義地查詢 AuthorisedUsers 然后再次查詢它,然后應用正確的 where 子句。我無法弄清楚為什么物體使用的 lambda 運算式會轉換為不需要的子選擇。
如果我在除錯器中檢查這兩個運算式的運算式(兩個運算式的除錯視圖),我們可以看到它們是相同的,除了一個微小的差異,但我不相信它會產生這種效果?我在我可以看到的潛在差異旁邊放了 2**,但除此之外,它似乎應該 100% 生成完全相同的 SQL?
感謝您對此的任何幫助!
---------------------錯誤的 SQL 除錯結果--------------------
除錯評估:
{x => value(DbContext).AuthorisedUsers.Any(b => ((b.RandomTableId == x.RandomTableId) AndAlso (b.UserId == value(DbContext).UserId)))} **System.Linq.Expressions.LambdaExpression {System.Linq.Expressions.Expression1<System.Func<Entities.RandomTable, bool>>}**
除錯視圖:
.Lambda #Lambda1<System.Func`2[Entities.RandomTable,System.Boolean]>(Entities.RandomTable$x)
{
.Call System.Linq.Queryable.Any(
.Constant<DbContext>(DbContext).AuthorisedUsers,
'(.Lambda #Lambda2<System.Func`2[Entities.AuthorisedUsers,System.Boolean]>))
}
.Lambda #Lambda2<System.Func`2[Entities.AuthorisedUsers,System.Boolean]>(Entities.AuthorisedUsers$b)
{
$b.RandomTableId == $x.RandomTableId && $b.UserId == .Constant<DbContext>(DbContext).UserId
}
---------------------良好的 SQL 除錯結果--------------------
除錯評估:
{c => value(DbContext).AuthorisedUsers.Any(b => ((b.RandomTableId == c.RandomTableId) AndAlso (b.UserId == value(DbContext).UserId)))} **System.Linq.Expressions.Expression<System.Func<Entities.RandomTable, bool>> {System.Linq.Expressions.Expression1<System.Func<Entities.RandomTable, bool>>}**
除錯視圖:
.Lambda #Lambda1<System.Func`2[Entities.RandomTable,System.Boolean]>(Entities.RandomTable $c)
{
.Call System.Linq.Queryable.Any(
.Constant<DbContext>(DbContext).AuthorisedUsers,
'(.Lambda #Lambda2<System.Func`2[Entities.AuthorisedUsers,System.Boolean]>))
}
.Lambda #Lambda2<System.Func`2[Entities.AuthorisedUsers,System.Boolean]>(Entities.AuthorisedUsers $b)
{
$b.RandomTableId == $c.RandomTableId && $b.UserId == .Constant<DbContext>(DbContext).UserId
}
運算式在 dbcontext 中像這樣應用:
foreach (Microsoft.EntityFrameworkCore.Metadata.IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes().Where(et => et.BaseType == null))
{
//Done like this to compare the same expression(hand built vs runtime) //in debugger
if (entityType.ClrType == typeof(RandomTable))
{
entityType.SetQueryFilter(test);
}
else
{
entityType.SetQueryFilter(testruntimeversion);
}
}
如果我們使用運算式可視化除錯工具,它會將兩個運算式轉換為相同的運算式,這有什么意義?!
(Entities.RandomTable c) => ProviderAccess.Any<Entities.AuthorisedUsers>(
// Quoted to induce a closure:
(Entities.AuthorisedUsers _) => (b.RandomTableId == c.RandomTableId) && (b.UserId == UserId))
錯誤的運算式創建:
//The below Expression Tree is equiv to the lambda:
Expression<Func<RandomTable, bool>> test = c => AuthorisedUsers.Any(b => b.RandomTableId == c.RandomTableId && b.UserId == GetUserId);
ParameterExpression b = Expression.Parameter(typeof(AuthorisedUsers), "b");
ParameterExpression x = Expression.Parameter(entityType.ClrType, "x");
MethodInfo anymethod = typeof(Queryable).GetMethods().Single(mi => mi.Name == "Any" && mi.GetParameters().Length == 2).MakeGenericMethod(typeof(AuthorisedUsers));
MemberExpression localUserId = Expression.MakeMemberAccess(Expression.Constant(this), typeof(DbContext).GetProperty(nameof(GetUserId), BindingFlags.Instance | BindingFlags.Public));
MemberExpression getAuthorisedUsers = Expression.MakeMemberAccess(
Expression.Constant(this), typeof(DbContext).GetProperty(nameof(AuthorisedUsers)));
MemberExpression randomtableid = Expression.MakeMemberAccess(b, typeof(AuthorisedUsers).GetProperty("RandomTableId"));
MemberExpression userid = Expression.MakeMemberAccess(b, typeof(AuthorisedUsers).GetProperty("UserId"));
BinaryExpression randomTableIdmatch = Expression.Equal(randomtableid,
Expression.Convert(Expression.MakeMemberAccess(x, entityType.ClrType.GetProperty(propertyName)), typeof(int)));
LambdaExpression g = Expression.Lambda(Expression.Call(anymethod, getAuthorisedUsers,
Expression.Quote(
Expression.Lambda(Expression.AndAlso(randomTableIdmatch,
Expression.Equal(userid, localUserId)
), b)
)
),
x
);
entityType.SetQueryFilter(g);
uj5u.com熱心網友回復:
您的不良運算式樹使用LambdaExpression,它是Expression<TDelegate>的基本型別(由您的良好運算式樹使用)。與Expression<TDelegate>不同, LambdaExpression表示非強型別的 lambda 運算式。
如果您希望構建的運算式樹具有Expression<TDelegate>您應該使用Expression.Lambda的多載之一,它采用委托型別。
注意。差異在 DebugView 中不可見,但請參閱此處了解 DebugView 語法
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/506995.html
