[英]EF Core throws Null Reference Exception when Null Check is done (Secondary Select with null check throws Null Reference Exception)
我有几个复杂的查询,通过反射映射的 generics 类自动映射 model 。
如果需要,我会在问题下方添加实体配置。
至于问题,我有一个通过多次传递构建的查询,一个传递在基本存储库中,另一个在实体特定存储库中。
但完成生成的查询将是这样的:
var x = await _uow.Knowledge.Query.Include(i => i.Translates)
.ThenInclude(i => i.Language)
.Select(s => new
{
Item = s,
TranslateNative = s.Translates != null //.Any()
? s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.Native.Iso6391)
: null,
TranslateEnglish = s.Translates != null //.Any()
? s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.English.Iso6391)
: null,
TranslateSystem = s.Translates != null //.Any()
? s.Translates.FirstOrDefault(w => w.LanguageId == SystemLanguageId)
: null,
TranslateAnyNativePriority = s.Translates != null //.Any()
? (
s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.Native.Iso6391)
?? (SystemLanguageId.HasValue
? s.Translates.FirstOrDefault(w => w.Language.Id == SystemLanguageId.Value)
: s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.English.Iso6391))
?? s.Translates.FirstOrDefault()
)
: null,
TranslateAnySystemPriority = s.Translates != null //.Any()
? (
(SystemLanguageId.HasValue
? s.Translates.FirstOrDefault(w => w.Language.Id == SystemLanguageId.Value)
: s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.Native.Iso6391))
?? s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.English.Iso6391)
?? s.Translates.FirstOrDefault()
)
: null
})
.Select(s=> new KnowledgeListVm
{
Id = s.Item.Id,
Name = s.TranslateAnySystemPriority != null ? s.TranslateAnySystemPriority.Name : null,
NameEn = s.TranslateEnglish != null ? s.TranslateEnglish.Name : null
})
.ToListAsync();
现在,当我添加一个只有本地翻译的新实体时,我收到以下异常,没有任何内部(InnerException):
System.NullReferenceException:“对象引用未设置为 object 的实例。”
即使我做了所有 null 检查。
因为我可以摆脱 Null 参考异常的事情,我自己会在答案中讲述 rest 的故事。 但是如果有人知道幕后发生了什么,请告诉我写一个更好的代码。 因为我认为我只是在我的代码中作弊。
public class Knowledge : IIdentityIdEntity<int>, IHasTranslateEntity<Knowledge, KnowledgeTranslate, int>
{
public Knowledge()
{
Translates = new HashSet<KnowledgeTranslate>();
CreatorKnowledges = new HashSet<CreatorKnowledge>();
}
public int Id { get; set; }
public ICollection<KnowledgeTranslate> Translates { get; set; }
public ICollection<CreatorKnowledge> CreatorKnowledges { get; set; }
}
public class KnowledgeTranslate : IIdentityIdEntity<int>, IIsTranslateEntity<Knowledge, KnowledgeTranslate, int>
{
public int Id { get; set; }
public int OwnerId { get; set; }
public Knowledge Owner { get; set; }
public int LanguageId { get; set; }
public Language Language { get; set; }
public string Name { get; set; }
}
public class Language: IIdentityIdEntity<int>, IHasTranslateEntity<Language, LanguageTranslate, int>
{
public Language()
{
Translates = new HashSet<LanguageTranslate>();
//Languages = new HashSet<LanguageTranslate>();
// we have so many joins for Languages ... with languageTranslate, with ProjectTranslate, with any kind of XTranslate but we don't need them
}
public int Id { get; set; }
public string Iso6391 { get; set; }
public string Iso6392T { get; set; }
public string Iso6392B { get; set; }
public string Iso6393 { get; set; }
/// <summary>
/// Translated information that one language have (Joined with LanguageId)
/// </summary>
public virtual ICollection<LanguageTranslate> Translates { get; set; }
///// <summary>
///// Language in which the translation is based on (Joined with OwnerId)
///// </summary>
//public virtual HashSet<LanguageTranslate> Languages { get; set; }
// we have so many joins for Languages ... with languageTranslate, with ProjectTranslate, with any kind of XTranslate but we don't need them
}
和映射器 map 他们像这样:
if ((interfaceType = entityType.ClrType.GetInterfaces()
.FirstOrDefault(w => w.IsGenericType
&& w.GetGenericTypeDefinition() == typeof(IHasTranslateEntity<,,>))) != null)
{
if (interfaceType.GetGenericArguments().Length != 3)
{
throw new NotImplementedException(@$"Cannot find implementation for ""{typeof(IHasTranslateEntity<,,>).Name}"" interface that take more than one argument in ""{nameof(ApplicationDbContext)}"" class.");
}
// var genericArgType = interfaceType.GenericTypeArguments[0]; // Here act same as ClrType
var translateArgType = interfaceType.GenericTypeArguments[1];
var idArgType = interfaceType.GenericTypeArguments[2];
builder.SetTranslatesMapping(entityType.ClrType, translateArgType, idArgType);
}
#region Translates
public static void SetTranslatesMapping(this ModelBuilder modelBuilder, Type entityType, Type translateEntityType, Type tId)
{
SetTranslatesMappingMethod.MakeGenericMethod(entityType, translateEntityType, tId)
.Invoke(null, new object[] { modelBuilder });
}
static readonly MethodInfo SetTranslatesMappingMethod = typeof(EFFilterExtensions)
.GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
.Single(t => t.IsGenericMethod && t.Name == nameof(SetTranslatesMapping));
private static void SetTranslatesMapping<TEntity, TTranslateEntity, TId>(this ModelBuilder modelBuilder)
where TEntity : class, IHasTranslateEntity<TEntity, TTranslateEntity, TId>
where TTranslateEntity : class, IIsTranslateEntity<TEntity, TTranslateEntity, TId>
{
// Is Duplicate
// modelBuilder.Entity<TEntity>().Property(e => e.Id);
// var translateType = modelBuilder.Entity<TEntity>().Property(p => p.Translates).Metadata.ClrType;
modelBuilder
.Entity<TEntity>()
.HasMany(m => m.Translates)
.WithOne(o => o.Owner)
.HasForeignKey(fk => fk.OwnerId)
.OnDelete(DeleteBehavior.Cascade);
}
#endregion Translates
我已经更新了好几次我的包,因为我没有时间,我碰巧看到了很多版本的 EfCore 3.1 包,现在我在 3.1.10 上。
所以每次我遇到我的项目时,我也访问了这个错误,因为我有一点时间,我最终检查了我所有的检查,发现它们都是正确的,并且没有任何改变就离开了项目。
今天,我有更多时间,我开始按照您在上面看到的那样一步一步地重新创建查询,我明白较大的查询不会失败( TranslateAnySystemPriority
)但较小的查询( TranslateEnglish
)
带有以下查询部分:
Select 1:
TranslateEnglish = s.Translates != null //.Any()
? s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.English.Iso6391)
: null,
Select 2:
NameEn = s.TranslateEnglish != null ? s.TranslateEnglish.Name : null
实际上,如果我说,问题出在第一个 Select 中返回 null 会更好。 我运行了几个测试,我发现:如果我在第一次或第二次Select
中对其进行一次空值检查,或者不对它运行任何空值检查,它将按我的预期工作,它工作正常。
所以我删除了所有 null 检查。
但是,如果,我对Select
进行 null 检查,只要从第一个select
的返回数据为空,它总是会抛出NullReferenceException
。 不管你做了多少次空检查。
所以固定代码是这样的:
NameEn = s.TranslateEnglish //!= null ? s.TranslateEnglish.Name : null
或者
TranslateEnglish = //s.Translates != null //.Any()
//?
s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.English.Iso6391)
//: null,
,
或者两者都做。
我的最终代码是:
var x = await _uow.Knowledge.Query.Include(i => i.Translates)
.ThenInclude(i => i.Language)
.Select(s => new
{
Item = s,
TranslateNative = s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.Native.Iso6391),
TranslateEnglish = s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.English.Iso6391),
TranslateSystem = s.Translates.FirstOrDefault(w => w.LanguageId == SystemLanguageId),
TranslateAnyNativePriority =
s.Translates.FirstOrDefault(w => w.Language.Iso6391 == FixedData.Language.Native.Iso6391)
?? (SystemLanguageId.HasValue
? s.Translates.FirstOrDefault(w => w.Language.Id == SystemLanguageId.Value)
: s.Translates.FirstOrDefault(w =>
w.Language.Iso6391 == FixedData.Language.English.Iso6391))
?? s.Translates.FirstOrDefault(),
TranslateAnySystemPriority = (SystemLanguageId.HasValue
? s.Translates.FirstOrDefault(w => w.Language.Id == SystemLanguageId.Value)
: s.Translates.FirstOrDefault(w =>
w.Language.Iso6391 == FixedData.Language.Native.Iso6391))
?? s.Translates.FirstOrDefault(
w => w.Language.Iso6391 == FixedData.Language.English.Iso6391)
?? s.Translates.FirstOrDefault()
})
.Select(s => new //KnowledgeListVm
{
Id = s.Item.Id,
Name = s.TranslateAnySystemPriority.Name,
NameEn = s.TranslateEnglish.Name
})
.ToListAsync();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.