当使用继承的对象时,我试图找到一种方法来过滤EF Core 2.1中的结果。
我有一个基本模型和几个继承的类(但是我只包含了一个):
public class Like {
public int Id { get; set; }
public LikeType LikeType { get; set; }
}
public class DocumentLike : Like {
[ForeignKey(nameof(Document))]
public int DocumentId { get; set; }
public virtual Document Document { get; set; }
}
LikeType
是一个枚举,被定义为dbcontext中的鉴别符。每个Document
都具有布尔属性.IsCurrent
。
要从数据库中获取所有项目,我正在使用类似这样的查询:
IQueryable<Like> query = _context.Set<Like>()
.Include(x => x.Owner)
.Include(x => (x as DocumentLike).Document.DocumentType)
.Include(x => (x as ProductLike).Product)
.Include(x => (x as TrainingLike).Training)
这很漂亮,并且返回所有包含子对象的对象,没有任何错误。我正在尝试从链接文档具有.IsCurrent == true
的数据库中获取所有项目。我尝试将以下内容添加到上面的查询中,但两者均导致异常:
.Where(x => (x as DocumentLike).Document.IsCurrent == true)
并且:
.Where(x => x.LikeType == LikeType.Document ? (x as DocumentLike).Document.IsCurrent == true : true)
执行查询时抛出的异常:
NullReferenceException: Object reference not set to an instance of an object.
lambda_method(Closure , TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<Like, ApplicationUser>, Organisation>, Training>, Product>, Platform>, NewsItem>, Event>, Document>, DocumentType>, Course>, CourseType>, ApplicationUser> )
System.Linq.Utilities+<>c__DisplayClass1_0<TSource>.<CombinePredicates>b__0(TSource x)
System.Linq.Enumerable+WhereSelectEnumerableIterator<TSource, TResult>.MoveNext()
Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider._TrackEntities<TOut, TIn>(IEnumerable<TOut> results, QueryContext queryContext, IList<EntityTrackingInfo> entityTrackingInfos, IList<Func<TIn, object>> entityAccessors)+MoveNext()
Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider+ExceptionInterceptor<T>+EnumeratorExceptionInterceptor.MoveNext()
System.Collections.Generic.List<T>.AddEnumerable(IEnumerable<T> enumerable)
System.Linq.Enumerable.ToList<TSource>(IEnumerable<TSource> source)
有没有办法做到这一点?
更新:
需要说明的是:我正在寻找一个查询,该查询从数据库返回所有Like
对象,而不管它们的(子)类型如何。如果子类型是DocumentLike
,我只希望链接到具有.IsCurrent == true
的文档的对象。
答案 0 :(得分:1)
我在类的多层层次结构中遇到了类似的问题,其中使用.OfType<>()
会导致数据库的“过早”访问(以我的观点),以获取所有数据,以便可以在内存中进行过滤,这是不可取的!
这说明了我的等级:
public abstract class BaseSetting {}
public abstract class AccountSetting : BaseSetting {}
public abstract class UserSetting : BaseSetting {}
public class AccountSettingA : AccountSetting {}
public class AccountSettingB : AccountSetting {}
public class UserSettingA : UserSetting {}
public class UserSettingB : UserSetting {}
这是DbContext
的设置:
public class DataContext : DbContext
{
public virtual DbSet<BaseSetting> Settings { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<BaseSetting>(e =>
{
e.ToTable("Settings");
e.HasDiscriminator<string>("Type");
});
}
}
然后,我将尝试获取单个帐户的所有设置,如下所示:
AccountSetting[] settings = context.Settings
.OfType<AccountSetting>()
.Where(s => s.Account.Id == accountId)
.ToArray();
这会导致类似以下的SQL查询:
SELECT *
FROM [Settings] AS [s0]
WHERE [s0].[Type] IN (N'AccountSettingA',N'AccountSettingB',N'UserSettingA',N'UserSettingB')
just before会在查询的NullReferenceException
位中抛出.Where(s => s.Account.Id == accountId)
,因为Account
为空。可以通过在查询中添加.Include(...)
来通过Account
来“修复”这一问题,但这只会增加我们从数据库中获取的大量数据。 (应该注意的是,如果按照@PanagiotisKanavos对原始问题的评论,尝试在客户端上进行评估时将上下文配置为抛出错误,那么您将在此处获得QueryClientEvaluationWarning
。)
解决方案(至少对我而言)是将其添加到我的OnModelCreating
中的DbContext
方法中:
typeof(BaseSetting).Assembly.GetTypes()
.Where(t => t != typeof(BaseSetting) && typeof(BaseSetting).IsAssignableFrom(t))
.Each(s => builder.Entity(s).HasBaseType(s.BaseType));
这将遍历我所有的不同设置类(继承自BaseSetting
),并告诉Entity Framework它们的基本类型是其Type.BaseType
。我本以为EF可以自己解决这个问题,但是这样做之后,我得到了这样的SQL(并且没有QueryClientEvaluationWarning
异常!):
SELECT *
FROM [Settings] as [a]
INNER JOIN [Accounts] AS [a.Account] ON [a].[AccountId] = [a.Account].[Id]
WHERE ([a].[Type] IN (N'AccountSettingA',N'AccountSettingB',N'UserSettingA',N'UserSettingB')
AND ([a.Account].[Id] = @__accountId)
显然,这只会返回我感兴趣的帐户的帐户设置,而不是全部帐户设置和所有用户设置像以前一样。
答案 1 :(得分:0)
您可以使用Enumerable.OfType筛选类型。有关更多信息,请查看https://docs.microsoft.com/de-de/dotnet/api/system.linq.enumerable.oftype?redirectedfrom=MSDN&view=netcore-2.1
对于您的情况,只需将结果过滤为
SELECT
st_distance(
point_column,
LEAD(point_column) OVER ()
)
FROM
table
答案 2 :(得分:0)
技巧是编辑谓词,如下所示:
.Where(x => !(x is DocumentLike) || ((DocumentLike)x).Document.IsCurrent == true)
感谢Panagiotis Kanavos的建议。