Làm cách nào để thay đổi truy vấn này để nó trả về tất cả u.usergroups?
from u in usergroups
from p in u.UsergroupPrices
select new UsergroupPricesList
{
UsergroupID = u.UsergroupID,
UsergroupName = u.UsergroupName,
Price = p.Price
};
Làm cách nào để thay đổi truy vấn này để nó trả về tất cả u.usergroups?
from u in usergroups
from p in u.UsergroupPrices
select new UsergroupPricesList
{
UsergroupID = u.UsergroupID,
UsergroupName = u.UsergroupName,
Price = p.Price
};
Câu trả lời:
được điều chỉnh từ MSDN, cách kết nối trái bằng EF 4
var query = from u in usergroups
join p in UsergroupPrices on u.UsergroupID equals p.UsergroupID into gj
from x in gj.DefaultIfEmpty()
select new {
UsergroupID = u.UsergroupID,
UsergroupName = u.UsergroupName,
Price = (x == null ? String.Empty : x.Price)
};
from x in gj.DefaultIfEmpty()
trở thành from p in gj.DefaultIfEmpty()
. docs.microsoft.com/en-us/ef/core/querying/…
Nó có thể hơi quá mức cần thiết, nhưng tôi đã viết một phương thức mở rộng, vì vậy bạn có thể thực hiện LeftJoin
bằng cách sử dụng Join
cú pháp (ít nhất là trong ký hiệu cuộc gọi phương thức):
persons.LeftJoin(
phoneNumbers,
person => person.Id,
phoneNumber => phoneNumber.PersonId,
(person, phoneNumber) => new
{
Person = person,
PhoneNumber = phoneNumber?.Number
}
);
Mã của tôi không làm gì khác hơn là thêm một GroupJoin
và một SelectMany
lệnh gọi vào cây biểu thức hiện tại. Tuy nhiên, nó trông khá phức tạp vì tôi phải tự xây dựng các biểu thức và sửa đổi cây biểu thức do người dùng chỉ định trong resultSelector
tham số để giữ cho toàn bộ cây có thể dịch được bởi LINQ-to-Entities.
public static class LeftJoinExtension
{
public static IQueryable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(
this IQueryable<TOuter> outer,
IQueryable<TInner> inner,
Expression<Func<TOuter, TKey>> outerKeySelector,
Expression<Func<TInner, TKey>> innerKeySelector,
Expression<Func<TOuter, TInner, TResult>> resultSelector)
{
MethodInfo groupJoin = typeof (Queryable).GetMethods()
.Single(m => m.ToString() == "System.Linq.IQueryable`1[TResult] GroupJoin[TOuter,TInner,TKey,TResult](System.Linq.IQueryable`1[TOuter], System.Collections.Generic.IEnumerable`1[TInner], System.Linq.Expressions.Expression`1[System.Func`2[TOuter,TKey]], System.Linq.Expressions.Expression`1[System.Func`2[TInner,TKey]], System.Linq.Expressions.Expression`1[System.Func`3[TOuter,System.Collections.Generic.IEnumerable`1[TInner],TResult]])")
.MakeGenericMethod(typeof (TOuter), typeof (TInner), typeof (TKey), typeof (LeftJoinIntermediate<TOuter, TInner>));
MethodInfo selectMany = typeof (Queryable).GetMethods()
.Single(m => m.ToString() == "System.Linq.IQueryable`1[TResult] SelectMany[TSource,TCollection,TResult](System.Linq.IQueryable`1[TSource], System.Linq.Expressions.Expression`1[System.Func`2[TSource,System.Collections.Generic.IEnumerable`1[TCollection]]], System.Linq.Expressions.Expression`1[System.Func`3[TSource,TCollection,TResult]])")
.MakeGenericMethod(typeof (LeftJoinIntermediate<TOuter, TInner>), typeof (TInner), typeof (TResult));
var groupJoinResultSelector = (Expression<Func<TOuter, IEnumerable<TInner>, LeftJoinIntermediate<TOuter, TInner>>>)
((oneOuter, manyInners) => new LeftJoinIntermediate<TOuter, TInner> {OneOuter = oneOuter, ManyInners = manyInners});
MethodCallExpression exprGroupJoin = Expression.Call(groupJoin, outer.Expression, inner.Expression, outerKeySelector, innerKeySelector, groupJoinResultSelector);
var selectManyCollectionSelector = (Expression<Func<LeftJoinIntermediate<TOuter, TInner>, IEnumerable<TInner>>>)
(t => t.ManyInners.DefaultIfEmpty());
ParameterExpression paramUser = resultSelector.Parameters.First();
ParameterExpression paramNew = Expression.Parameter(typeof (LeftJoinIntermediate<TOuter, TInner>), "t");
MemberExpression propExpr = Expression.Property(paramNew, "OneOuter");
LambdaExpression selectManyResultSelector = Expression.Lambda(new Replacer(paramUser, propExpr).Visit(resultSelector.Body), paramNew, resultSelector.Parameters.Skip(1).First());
MethodCallExpression exprSelectMany = Expression.Call(selectMany, exprGroupJoin, selectManyCollectionSelector, selectManyResultSelector);
return outer.Provider.CreateQuery<TResult>(exprSelectMany);
}
private class LeftJoinIntermediate<TOuter, TInner>
{
public TOuter OneOuter { get; set; }
public IEnumerable<TInner> ManyInners { get; set; }
}
private class Replacer : ExpressionVisitor
{
private readonly ParameterExpression _oldParam;
private readonly Expression _replacement;
public Replacer(ParameterExpression oldParam, Expression replacement)
{
_oldParam = oldParam;
_replacement = replacement;
}
public override Expression Visit(Expression exp)
{
if (exp == _oldParam)
{
return _replacement;
}
return base.Visit(exp);
}
}
}
Hãy làm cho cuộc sống của bạn dễ dàng hơn (không sử dụng tham gia vào nhóm):
var query = from ug in UserGroups
from ugp in UserGroupPrices.Where(x => x.UserGroupId == ug.Id).DefaultIfEmpty()
select new
{
UserGroupID = ug.UserGroupID,
UserGroupName = ug.UserGroupName,
Price = ugp != null ? ugp.Price : 0 //this is to handle nulls as even when Price is non-nullable prop it may come as null from SQL (result of Left Outer Join)
};
Price = ugp.Price
có thể thất bại nếu Price
là một thuộc tính không thể nullable và phép nối bên trái không cho kết quả nào.
ugp == NULL
và đặt giá trị mặc định cho Price
.
Nếu bạn thích ký hiệu cuộc gọi phương thức, bạn có thể buộc nối trái bằng cách sử dụng SelectMany
kết hợp với DefaultIfEmpty
. Ít nhất trên Entity Framework 6 đánh SQL Server. Ví dụ:
using(var ctx = new MyDatabaseContext())
{
var data = ctx
.MyTable1
.SelectMany(a => ctx.MyTable2
.Where(b => b.Id2 == a.Id1)
.DefaultIfEmpty()
.Select(b => new
{
a.Id1,
a.Col1,
Col2 = b == null ? (int?) null : b.Col2,
}));
}
(Lưu ý rằng đó MyTable2.Col2
là một cột của loại int
). SQL được tạo sẽ giống như sau:
SELECT
[Extent1].[Id1] AS [Id1],
[Extent1].[Col1] AS [Col1],
CASE WHEN ([Extent2].[Col2] IS NULL) THEN CAST(NULL AS int) ELSE CAST( [Extent2].[Col2] AS int) END AS [Col2]
FROM [dbo].[MyTable1] AS [Extent1]
LEFT OUTER JOIN [dbo].[MyTable2] AS [Extent2] ON [Extent2].[Id2] = [Extent1].[Id1]
Đối với 2 lần tham gia bên trái trở lên (người tạo tham gia bên trái và người khởi tạo người dùng)
IQueryable<CreateRequestModel> queryResult = from r in authContext.Requests
join candidateUser in authContext.AuthUsers
on r.CandidateId equals candidateUser.Id
join creatorUser in authContext.AuthUsers
on r.CreatorId equals creatorUser.Id into gj
from x in gj.DefaultIfEmpty()
join initiatorUser in authContext.AuthUsers
on r.InitiatorId equals initiatorUser.Id into init
from x1 in init.DefaultIfEmpty()
where candidateUser.UserName.Equals(candidateUsername)
select new CreateRequestModel
{
UserName = candidateUser.UserName,
CreatorId = (x == null ? String.Empty : x.UserName),
InitiatorId = (x1 == null ? String.Empty : x1.UserName),
CandidateId = candidateUser.UserName
};
Tôi đã có thể thực hiện việc này bằng cách gọi DefaultIfEmpty () trên mô hình chính. Điều này cho phép tôi rời tham gia trên các thực thể được tải chậm, có vẻ dễ đọc hơn đối với tôi:
var complaints = db.Complaints.DefaultIfEmpty()
.Where(x => x.DateStage1Complete == null || x.DateStage2Complete == null)
.OrderBy(x => x.DateEntered)
.Select(x => new
{
ComplaintID = x.ComplaintID,
CustomerName = x.Customer.Name,
CustomerAddress = x.Customer.Address,
MemberName = x.Member != null ? x.Member.Name: string.Empty,
AllocationName = x.Allocation != null ? x.Allocation.Name: string.Empty,
CategoryName = x.Category != null ? x.Category.Ssl_Name : string.Empty,
Stage1Start = x.Stage1StartDate,
Stage1Expiry = x.Stage1_ExpiryDate,
Stage2Start = x.Stage2StartDate,
Stage2Expiry = x.Stage2_ExpiryDate
});
.DefaultIfEmpty()
gì cả: nó chỉ ảnh hưởng đến những gì xảy ra khi db.Complains
trống. db.Complains.Where(...).OrderBy(...).Select(x => new { ..., MemberName = x.Member != null ? x.Member.Name : string.Empty, ... })
, nếu không có bất kỳ .DefaultIfEmpty()
, sẽ thực hiện một phép nối bên trái (giả sử thuộc Member
tính được đánh dấu là tùy chọn).
Nếu Nhóm người dùng có mối quan hệ từ một đến nhiều với bảng UserGroupPrices, thì trong EF, khi mối quan hệ được xác định trong mã như:
//In UserGroups Model
public List<UserGroupPrices> UserGrpPriceList {get;set;}
//In UserGroupPrices model
public UserGroups UserGrps {get;set;}
Bạn có thể kéo tập kết quả nối bên trái bằng cách đơn giản sau:
var list = db.UserGroupDbSet.ToList();
giả sử DbSet của bạn cho bảng bên trái là UserGroupDbSet, sẽ bao gồm UserGrpPriceList, là danh sách tất cả các bản ghi được liên kết từ bảng bên phải.