我的 Linq 查询在 EF Core 3.1 中是最优的和高性能的吗?
Is my Linq Query Optimal and High performance in EF Core 3.1?
我可以删除 vg.First().Voucher 吗?并替换更好的代码?最佳和最佳实践是什么?
可以将此代码转换为另一种方法吗?喜欢连锁法?
var query = from v in _journalLineRepository.TableNoTracking
.Include(j => j.Voucher).AsEnumerable()
group v by v.AccountId into vg
select new // <-- temporary projection with group by fields needed
{
AccountId = vg.Key,
Credit = vg.Sum(v => v.Credit),
Debit = vg.Sum(v => v.Debit),
Voucher = vg.First().Voucher
} into vg
join p in _partyRepository.TableNoTracking.Include(p => p.PartyPhones).AsEnumerable() on vg.AccountId equals p.AccountId // <-- additional join(s)
select new PartyDeptorAndCreditorViewModel
{
PartyId = p.Id,
FullName = p.FullName,
PhoneNo = p.PartyPhones.FirstOrDefault(p => p.IsActive)?.Phone,
ProjectId = vg.Voucher.ProjectId,
AccountId = vg.AccountId.Value,
Creditor = vg.Credit,
Deptor = vg.Debit,
Balance = vg.Credit - vg.Debit,
VoucherDate = vg.Voucher.VoucherDate,
VoucherRegisterDate = vg.Voucher.VoucherDate,
BalanceType =
vg.Debit > vg.Credit ? AccountingComplexEnum.ShowPartyBalanceParamSearch.Deptor.ToDisplay(DisplayProperty.Name) :
vg.Debit < vg.Credit ? AccountingComplexEnum.ShowPartyBalanceParamSearch.Creditor.ToDisplay(DisplayProperty.Name) :
AccountingComplexEnum.ShowPartyBalanceParamSearch.ZeroBalance.ToDisplay(DisplayProperty.Name),
};
我肯定会查看生成的 SQL 查询。从表面上看,我看到一些警告标志,它可能不是在编写查询,而是可能预执行到内存中处理,这将是低效的。它首先取决于这些 .TableNoTracking
methods/properties return,以及 .AsEnumerable
在 eager load joins 上的使用。
首先,使用 Select
进行投影时,不需要预加载连接 (.Include
)。投影将为您处理连接,前提是它向下投影到 SQL。如果您删除了 .Include().AsEnumerable()
调用并且您的查询仍然有效,那么它很可能会下降到 SQL。如果它不再工作,那么它正在内存中处理,效率不高。
编辑:不,内部投影无法解析:关于 .Voucher
,你的最终投影使用了这个实体的 2 个字段,所以你可以在初始预测:
select new // <-- temporary projection with group by fields needed
{
AccountId = vg.Key,
Credit = vg.Sum(v => v.Credit),
Debit = vg.Sum(v => v.Debit),
Voucher = vg.Select(v => new { v.ProjectId, v.VoucherDate }).First()
} into vg
当涉及到这样的转换时:
BalanceType = vg.Debit > vg.Credit
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Deptor.ToDisplay(DisplayProperty.Name)
: vg.Debit < vg.Credit
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Creditor.ToDisplay(DisplayProperty.Name)
: AccountingComplexEnum.ShowPartyBalanceParamSearch.ZeroBalance.ToDisplay(DisplayProperty.Name),
... 在投影中,这会发出警告标志,因为 Linq2EF 需要将投影组合成 SQL,因此 methods/extensions 就像 ToDisplay
不会被理解。相反,由于这完全基于 Credit/Debit 数量,我会将其移动到由视图模型中的 属性 计算:
select new PartyDeptorAndCreditorViewModel
{
PartyId = p.Id,
FullName = p.FullName,
PhoneNo = p.PartyPhones
.Where(p => p.IsActive)
.Select(p => p.Phone)
.FirstOrDefault(),
ProjectId = vg.Voucher.ProjectId,
AccountId = vg.AccountId.Value,
Creditor = vg.Credit,
Deptor = vg.Debit,
Balance = vg.Credit - vg.Debit,
VoucherDate = vg.Voucher.VoucherDate,
VoucherRegisterDate = vg.Voucher.VoucherDate
};
然后在视图模型中:
[Serializable]
public class PartyDebtorAndCreditorViewModel
{
// ...
public decimal Balance { get; set; }
public string BalanceType
{
get
{
return Balance < 0
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Deptor.ToDisplay(DisplayProperty.Name)
: Balance > 0
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Creditor.ToDisplay(DisplayProperty.Name)
: AccountingComplexEnum.ShowPartyBalanceParamSearch.ZeroBalance.ToDisplay(DisplayProperty.Name);
}
}
}
我可以删除 vg.First().Voucher 吗?并替换更好的代码?最佳和最佳实践是什么? 可以将此代码转换为另一种方法吗?喜欢连锁法?
var query = from v in _journalLineRepository.TableNoTracking
.Include(j => j.Voucher).AsEnumerable()
group v by v.AccountId into vg
select new // <-- temporary projection with group by fields needed
{
AccountId = vg.Key,
Credit = vg.Sum(v => v.Credit),
Debit = vg.Sum(v => v.Debit),
Voucher = vg.First().Voucher
} into vg
join p in _partyRepository.TableNoTracking.Include(p => p.PartyPhones).AsEnumerable() on vg.AccountId equals p.AccountId // <-- additional join(s)
select new PartyDeptorAndCreditorViewModel
{
PartyId = p.Id,
FullName = p.FullName,
PhoneNo = p.PartyPhones.FirstOrDefault(p => p.IsActive)?.Phone,
ProjectId = vg.Voucher.ProjectId,
AccountId = vg.AccountId.Value,
Creditor = vg.Credit,
Deptor = vg.Debit,
Balance = vg.Credit - vg.Debit,
VoucherDate = vg.Voucher.VoucherDate,
VoucherRegisterDate = vg.Voucher.VoucherDate,
BalanceType =
vg.Debit > vg.Credit ? AccountingComplexEnum.ShowPartyBalanceParamSearch.Deptor.ToDisplay(DisplayProperty.Name) :
vg.Debit < vg.Credit ? AccountingComplexEnum.ShowPartyBalanceParamSearch.Creditor.ToDisplay(DisplayProperty.Name) :
AccountingComplexEnum.ShowPartyBalanceParamSearch.ZeroBalance.ToDisplay(DisplayProperty.Name),
};
我肯定会查看生成的 SQL 查询。从表面上看,我看到一些警告标志,它可能不是在编写查询,而是可能预执行到内存中处理,这将是低效的。它首先取决于这些 .TableNoTracking
methods/properties return,以及 .AsEnumerable
在 eager load joins 上的使用。
首先,使用 Select
进行投影时,不需要预加载连接 (.Include
)。投影将为您处理连接,前提是它向下投影到 SQL。如果您删除了 .Include().AsEnumerable()
调用并且您的查询仍然有效,那么它很可能会下降到 SQL。如果它不再工作,那么它正在内存中处理,效率不高。
编辑:不,内部投影无法解析:关于 .Voucher
,你的最终投影使用了这个实体的 2 个字段,所以你可以在初始预测:
select new // <-- temporary projection with group by fields needed
{
AccountId = vg.Key,
Credit = vg.Sum(v => v.Credit),
Debit = vg.Sum(v => v.Debit),
Voucher = vg.Select(v => new { v.ProjectId, v.VoucherDate }).First()
} into vg
当涉及到这样的转换时:
BalanceType = vg.Debit > vg.Credit
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Deptor.ToDisplay(DisplayProperty.Name)
: vg.Debit < vg.Credit
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Creditor.ToDisplay(DisplayProperty.Name)
: AccountingComplexEnum.ShowPartyBalanceParamSearch.ZeroBalance.ToDisplay(DisplayProperty.Name),
... 在投影中,这会发出警告标志,因为 Linq2EF 需要将投影组合成 SQL,因此 methods/extensions 就像 ToDisplay
不会被理解。相反,由于这完全基于 Credit/Debit 数量,我会将其移动到由视图模型中的 属性 计算:
select new PartyDeptorAndCreditorViewModel
{
PartyId = p.Id,
FullName = p.FullName,
PhoneNo = p.PartyPhones
.Where(p => p.IsActive)
.Select(p => p.Phone)
.FirstOrDefault(),
ProjectId = vg.Voucher.ProjectId,
AccountId = vg.AccountId.Value,
Creditor = vg.Credit,
Deptor = vg.Debit,
Balance = vg.Credit - vg.Debit,
VoucherDate = vg.Voucher.VoucherDate,
VoucherRegisterDate = vg.Voucher.VoucherDate
};
然后在视图模型中:
[Serializable]
public class PartyDebtorAndCreditorViewModel
{
// ...
public decimal Balance { get; set; }
public string BalanceType
{
get
{
return Balance < 0
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Deptor.ToDisplay(DisplayProperty.Name)
: Balance > 0
? AccountingComplexEnum.ShowPartyBalanceParamSearch.Creditor.ToDisplay(DisplayProperty.Name)
: AccountingComplexEnum.ShowPartyBalanceParamSearch.ZeroBalance.ToDisplay(DisplayProperty.Name);
}
}
}