EntityFrameworkCore выдает исключение NullReferenceException при запросе данных

#oracle #asp.net-core #oracle11g #entity-framework-core

#Oracle #asp.net-ядро #оракул11g #сущность-структура-ядро

Вопрос:

Я использую EF Core 2.2 для подключения к базе данных Oracle11g (использование этих старых версий является обязательным), когда я пытаюсь запросить одну из своих таблиц, она выдает a NullReferenceException во внешнем коде EF Core. Запрос:

 string username = "Administrator";
var user = _context.Users.Where(x => x.UserName.ToUpper() == username.ToUpper())
                    .Include(x => x.UserProfile)
                    .Include(x => x.UserRoleOrganizations).ThenInclude(s => s.Role)
                    .Include(x => x.UserRoleOrganizations).ThenInclude(s => s.Organization);

return user.FirstOrDefault();
 

Исключение:

 System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=Microsoft.EntityFrameworkCore.Relational
  StackTrace:
   at Microsoft.EntityFrameworkCore.Storage.TypedRelationalValueBufferFactoryFactory.CreateGetValueExpression(Expression dataReaderExpression, Int32 index, TypeMaterializationInfo materializationInfo, Boolean detailedErrorsEnabled, Boolean box)
   at Microsoft.EntityFrameworkCore.Storage.TypedRelationalValueBufferFactoryFactory.<>c__DisplayClass13_0.<CreateArrayInitializer>b__0(TypeMaterializationInfo mi, Int32 i)
   at System.Linq.Enumerable.<SelectIterator>d__5`2.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Dynamic.Utils.CollectionExtensions.ToReadOnly[T](IEnumerable`1 enumerable)
   at System.Linq.Expressions.Expression.NewArrayInit(Type type, IEnumerable`1 initializers)
   at Microsoft.EntityFrameworkCore.Storage.TypedRelationalValueBufferFactoryFactory.CreateArrayInitializer(CacheKey cacheKey, Boolean detailedErrorsEnabled)
   at Microsoft.EntityFrameworkCore.Storage.TypedRelationalValueBufferFactoryFactory.<Create>b__11_0(CacheKey k)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValueamp; target, TParam param, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.NotifyReaderCreated(DbDataReader dataReader)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.BufferlessMoveNext(DbContext _, Boolean buffer)
   at Oracle.EntityFrameworkCore.Storage.Internal.OracleExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ResultEnumerable`1.GetEnumerator()
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.<_TrackEntities>d__17`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.LinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext()
   at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass15_1`1.<CompileQueryCore>b__0(QueryContext qc)
   at Base.Security.Business.UserManagement.GetUser(String username) in C:UsersAfagh2sourcereposHSE2_OracleBase.SecurityBusinessUserManagement.cs:line 88
 

Сгенерированный запрос:

 [Parameters=[:ToUpper_0='ADMINISTRATOR' (Size = 256)], CommandType='Text', CommandTimeout='0']
Select
K0 "Id", K1 "AccessFailedCount", K2 "ConcurrencyStamp", K3 "Email", K4 "EmailConfirmed", K5 "FirstName", K6 "IsActive", K7 "IsExternal", K8 "LastName", K9 "LockoutEnabled", K10 "LockoutEnd", K11 "NationalCode", K12 "NormalizedEmail", K13 "NormalizedUserName", K14 "OrganizationName", K15 "OrganizationType", K16 "PasswordHash", K17 "PersonnelCode", K18 "PhoneNumber", K19 "PhoneNumberConfirmed", K20 "PositionName", K21 "SecurityStamp", K22 "TwoFactorEnabled", K23 "UserName", K24 "Id", K25 "FileContent", K26 "FileExtension", K27 "FileName" from(
  SELECT "x"."Id" K0, "x"."AccessFailedCount" K1, "x"."ConcurrencyStamp" K2, "x"."Email" K3, "x"."EmailConfirmed" K4, "x"."FirstName" K5, "x"."IsActive" K6, "x"."IsExternal" K7, "x"."LastName" K8, "x"."LockoutEnabled" K9, "x"."LockoutEnd" K10, "x"."NationalCode" K11, "x"."NormalizedEmail" K12, "x"."NormalizedUserName" K13, "x"."OrganizationName" K14, "x"."OrganizationType" K15, "x"."PasswordHash" K16, "x"."PersonnelCode" K17, "x"."PhoneNumber" K18, "x"."PhoneNumberConfirmed" K19, "x"."PositionName" K20, "x"."SecurityStamp" K21, "x"."TwoFactorEnabled" K22, "x"."UserName" K23, "x.UserProfile"."Id" K24, "x.UserProfile"."FileContent" K25, "x.UserProfile"."FileExtension" K26, "x.UserProfile"."FileName" K27
  FROM "AppUser" "x"
  LEFT JOIN "AppUserProfilePicture" "x.UserProfile" ON ("x"."Id" = "x.UserProfile"."Id")
  WHERE ("UPPER"("x"."UserName") = :ToUpper_0)
  ORDER BY "x"."Id"
) "m1"
where rownum <= 1
 

Запрос работает так, как ожидалось в Oracle SQL Developer. Похоже, что у EF Core возникли некоторые проблемы с преобразованием результатов в объект (который в данном случае должен быть none, потому что в таблице нет данных.), Но я не могу отладить его код. (Или я могу?)
Объект настроен в OnModelCreating :

 builder.Entity<ApplicationUser>(b =>
{
    b.HasKey(u => u.Id);
    b.HasIndex(u => u.NormalizedUserName).HasName("UserNameIndex").IsUnique();
    b.HasIndex(u => u.NormalizedEmail).HasName("EmailIndex");
    b.ToTable("AppUser");
    b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();

    b.Property(u => u.UserName).HasMaxLength(256);
    b.Property(u => u.NormalizedUserName).HasMaxLength(256);
    b.Property(u => u.Email).HasMaxLength(256);
    b.Property(u => u.NormalizedEmail).HasMaxLength(256);

    b.HasOne(x => x.UserProfile).WithOne(x => x.User).HasForeignKey<ApplicationUserProfilePicture>(x => x.Id);
    b.HasMany<IdentityUserClaim<int>>().WithOne().HasForeignKey(uc => uc.UserId).IsRequired();
    b.HasMany<IdentityUserLogin<int>>().WithOne().HasForeignKey(ul => ul.UserId).IsRequired();
    b.HasMany<IdentityUserToken<int>>().WithOne().HasForeignKey(ut => ut.UserId).IsRequired();
    b.HasMany<ApplicationUserRoleOrganization>().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
});
 

Я могу создавать и запрашивать другие таблицы. AppUser В Oracle не работает только таблица, она отлично работает с использованием поставщика SQL Server.
Скажите мне, нужна ли дополнительная информация о чем-либо.


Обновить
Класс пользователя:

 public class ApplicationUser : Microsoft.AspNetCore.Identity.IdentityUser<int>
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    [StringLength(450)]
    public string PositionName { get; set; }
    [StringLength(450)]
    public string NationalCode { get; set; }
    [StringLength(450)]
    public string PersonnelCode { get; set; }
    public bool IsActive { get; set; } = true;
    public bool IsExternal { get; set; } = false;
    [Required]
    public OrganizationTypeEnum OrganizationType { get; set; }
    public string OrganizationName { get; set; }
    public virtual ApplicationUserProfilePicture UserProfile { get; set; }
    public virtual ICollection<ApplicationUserRoleOrganization> UserRoleOrganizations { get; set; }
}
 

Класс UserRoleOrganization:

 public class ApplicationUserRoleOrganization : Microsoft.AspNetCore.Identity.IdentityUserRole<int>
{
    public int Id { get; set; }
    public override int RoleId { get => base.RoleId; set => base.RoleId = value; }
    public override int UserId { get => base.UserId; set => base.UserId = value; }
    public virtual int OrganizationId { get; set; }
    public virtual ApplicationUser User { get; set; }
    public virtual ApplicationRole Role { get; set; }
    public virtual Organization Organization { get; set; }
}
 

Конфигурация UserRoleOrganization:

 builder.Entity<ApplicationUserRoleOrganization>(b =>
{
    b.HasKey(r => r.Id);
    b.HasIndex(t => t.OrganizationId).HasName("OrganizationIndex");
    b.HasIndex(t => t.RoleId).HasName("RoleIndex");
    b.HasIndex(t => t.UserId).HasName("UserIndex");
    b.HasOne(userRole => userRole.Role)
    .WithMany(role => role.UserRoleOrganizations)
    .HasForeignKey(userRole => userRole.RoleId).HasConstraintName("FK_RoleOrg_Role_RoleId");
    b.HasOne(userRole => userRole.Organization)
    .WithMany(role => role.UserRoleOrganizations)
    .HasForeignKey(userRole => userRole.OrganizationId).HasConstraintName("FK_RoleOrg_Org_OrgId");
    b.HasOne(userRole => userRole.User)
    .WithMany(user => user.UserRoleOrganizations)
    .HasForeignKey(userRole => userRole.UserId).HasConstraintName("FK_RoleOrg_User_UserId");
    b.ToTable("AppUserRoleOrganization");
});
 

Я только что заметил, что текст написан в моем выводе непосредственно перед исключением:

 fail: Microsoft.EntityFrameworkCore.Query[10100]
      An exception occurred while iterating over the results of a query for context type 'Base.Security.Data.SecurityDbContext'.
 

Комментарии:

1. во-первых, вы уверены, что исключение генерируется из отправленного вами запроса? попробуйте удалять части этого запроса, пока он не станет простейшей формой (например: _context.Users ), чтобы выяснить, что может вызвать исключение (именно так мы решаем проблему сами, вызывается очень известный метод trial and error ). Это может избавить вас от вопроса, опубликованного в SO, или, по крайней мере, предоставить нам более ценную информацию. Во-вторых, можете ли вы легко воспроизвести ошибку, есть ли какие-либо потоки?

2. Я уверен, что проверяю простой запрос, прежде чем задавать такой большой и конкретный вопрос в SO. Даже _context.Users.ToList() произойдет сбой. И что касается второй части, я не понимаю, почему я должен воспроизводить ошибку, поскольку она всегда есть.

3. если _context.Users.ToList() также произошел сбой, это должен был быть запрос, о котором вы спрашивали, потому что это намного проще, поэтому мы можем сосредоточиться на других возможностях, а не на самом подробном запросе. Затем вы должны попробовать получить другие наборы БД для этого _context , чтобы увидеть, выдается ли ошибка. Кстати, это кажется очень странным, потому _context.Users.ToList() что это настолько просто, что в другом месте в запросе было бы что-то не так. NullReferenceException В этом случае это также не имеет особого смысла, что делает его еще более трудным для понимания.

Ответ №1:

Вы дважды включили UserRoleOrganizations . Попробуйте сделать в том числе старым добрым способом:

 string username = "Administrator";
return _context.Users.Where(x => x.UserName.ToUpper() == username.ToUpper())
                    .Include(x => x.UserProfile)
                    .Include("UserRoleOrganizations.Organization");
                    .Include("UserRoleOrganizations.Role")
                    .FirstOrDefault();
 

Но мне не нравятся пользовательские ролевые организации.Организация . Не могли бы вы показать свои классы User и UserRoleOrganizations?

Обновить.

Поскольку я вижу классы, я думаю, вы можете попытаться отменить запрос, возможно, просто для тестирования:

 
var userRoleOrganizations=_context.UserRoleOrganizations
.Where(x => x.User.UserName.ToUpper() == username.ToUpper())
.Include(s => s.Role)
.Include(s => s.Organization)
.ToList();

var user = _context.Users
.Where(x => x.UserName.ToUpper() == username.ToUpper())
.Include(x => x.UserProfile)
.FirstOrDefault();

user.UserRoleOrganizations=userRoleOrganizations;

return user;

 

И попробуйте изменить класс UserRoleOrganizations:

 public class UserRoleOrganizations
{
....
public  override int? RoleId { get => base.RoleId; set => base.RoleId = value; }
 public override int? UserId { get => base.UserId; set => base.UserId = value; }
 public virtual int? OrganizationId { get; set; }
....
}
   

 

Комментарии:

1. Нет проблем с включением таблицы дважды, вы видели, что сгенерированный запрос правильный и простой. Даже _context.Users.ToList() если эта таблица выдаст то же исключение, произойдет сбой. Все, что я пытаюсь сделать с этой таблицей, завершится неудачей (кроме ее создания). Я обновил вопрос, чтобы предоставить запрошенную вами информацию.

2. Спасибо. Но я все еще не вижу класс User.

3. Спасибо за ответ, запрос по-прежнему выдает NRE. Изменение пользовательских ролей тоже не помогло. Даже если первый метод поможет, библиотека идентификации (Microsoft. AspNetCore. Идентификатор. В UserManager) есть метод с именем CreateAsync, используемый для создания пользователя, который запускает запрос в AppUser таблице, чтобы проверить, существует ли имя пользователя, поэтому я все равно буду получать NRE и не смогу отменить запрос.