如何使EF.net过滤SQL Server上而不是C#上的所有数据



我使用实体框架进行了一些测试。净核心。我打开了SQL server配置文件,开始比较我的查询和EF在SQL server上执行的内容。

这是我的模态类:

public class Offer : BaseModel
{
public DateTime HostStartDate { get; set; }
public DateTime HostEndDate { get; set; }
public int Guests { get; set; }
public decimal AmountOffer { get; set; }
public OfferStatus Status { get; set; }
public PaymentStatus PaymentStatus { get; set; }
public long CityId { get; set; }
public virtual City City { get; set; }
[Required]
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
public bool IsDeleted { get; set; }
[NotMapped]
public bool IsVisible => HostStartDate.Date >= DateTime.Today.Date;

public void SetSoftDelete()
{
IsDeleted = true;
Bids.ToList().ForEach(n => n.SetSoftDelete());
}
}
public class Bid : BaseModel , IStateAware
{
[NotMapped]
public override long Id { get; set; }
public long OfferId { get; set; }
public virtual Offer Offer { get; set; }
public long PropertyId { get; set; }
public virtual Property Property { get; set; }
public DateTime? Accepted { get; set; }
public bool Read { get; set; }
public bool IsDeleted { get; set; }
[NotMapped]
public ModelState State { get; set; }
[NotMapped]
public bool IsPayed => Accepted.HasValue;
[NotMapped]
public bool IsAccepted => Accepted.IsNotNull();
public void SetSoftDelete()
{
IsDeleted = true;
State = ModelState.Modified;
}
}

我有一个通用类来进行查找,如下所示:

public IQueryable<TEntity> FindBy(
Expression<Func<TEntity, bool>> filter = null,
Expression<Func<TEntity, object>> includeProperty = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null)
{
IQueryable<TEntity> query = context.Set<TEntity>();
if (filter.IsNotNull())
{
query = query.Where(filter);
}
if (includeProperty.IsNotNull()) 
{
query = query.Include(includeProperty);
}
if (orderBy.IsNotNull())
{
query = orderBy(query);
}
return query.AsNoTracking();
}

我这样称呼它:

var finalEnd = new DateTime(end.Year, end.Month, end.Day, 23, 59, 59);
var data = await _bidRepo.FindBy(n => !n.OfferId.Equals(offerId)
&& n.PropertyId.Equals(propertyId)
&& !n.IsDeleted
&& !n.IsAccepted
&& !n.IsPayed
&& !n.IsInvalidate
&& ((n.Offer.HostStartDate.Date >= start.Date && n.Offer.HostStartDate.Date <= end.Date) ||
(n.Offer.HostEndDate > finalEnd && n.Offer.HostEndDate.Date <= end.Date))
).ToListAsync();

我的查询返回4个项目,它是好的。但是,在SQL server Profile上,我的查询没有处理任何不同的日期。

这是配置文件:

exec sp_executesql N'SELECT [n].[OfferId], [n].[PropertyId], [n].[Accepted], [n].[CreatedBy], [n].[CreatedDate], [n].[IsDeleted], [n].[IsInvalidate], [n].[ModifiedBy], [n].[ModifiedDate], [n].[Read]
FROM [Bids] AS [n]
INNER JOIN [Offers] AS [n.Offer] ON [n].[OfferId] = [n.Offer].[Id]
WHERE ((CONVERT(date, [n.Offer].[HostStartDate]) >= @__start_Date_2) AND (CONVERT(date, [n.Offer].[HostStartDate]) <= @__end_Date_3)) OR (([n.Offer].[HostEndDate] > @__finalEnd_4) AND (CONVERT(date, [n.Offer].[HostEndDate]) <= @__end_Date_3))',N'@__start_Date_2 datetime2(7),@__end_Date_3 datetime2(7),@__finalEnd_4 datetime2(7)',@__start_Date_2='2020-02-23 00:00:00',@__end_Date_3='2020-02-25 00:00:00',@__finalEnd_4='2020-02-25 23:59:59'

在这个SQL server语句中,我看不到任何布尔筛选器或Id。

如何确保所有子句都在SQL server上执行,而不是将部分日期带到C#,然后再将其删除?

这里有一些EF Core实现的怪癖,但主要问题是您使用的是无法转换为SQL的非映射/仅获取属性。EF Core 3.0+只会抛出运行时异常,但EF Core 1.x/2.x会在从数据库检索数据后在内存中评估这些条件。

为什么它们不能翻译?因为EF Core不是一个编译器,它看到的只是类似的东西

public bool IsPayed { get; }

为了可翻译,EF Core必须"查看"查询表达式树内部的实现。这意味着基本上你不能在LINQ to Entities查询中使用这样的属性(以及像IsNotNull这样的自定义方法(,应该直接对它们进行编码,例如,而不是

&& !n.IsPayed

你应该使用

&& !n.Accepted.HasValue

类似于&& !n.IsAccepted(以及最可能的!n.IsInvalidate(。

当然,这是代码重复,可读性较差,但这是IQueryable表达式树的一个常见问题,它没有编译器或BCL解决方案。有一些第三方库试图解决这个问题。例如,来自NeilLinq的Lambda注入。如果您坚持在数据模型/查询中使用OOP,请考虑使用其中一些第三方库,因为您不会从C#/BCL/EF Core中获得它。

最新更新