I'm using EF Core 2.2 to connect to an Oracle11g database (using these old versions is a must), when I try to query one of my tables it throws a NullReferenceException
in the EF Core external code.
The Query:
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();
The exception:
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](TValue& 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:\Users\Afagh2\source\repos\HSE2_Oracle\Base.Security\Business\UserManagement.cs:line 88
The generated query:
[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
The query works as expected in Oracle SQL Developer. It seems that EF Core has some trouble converting the results to the entity (which in this case, the result should be none because the table has no data.) but I can not debug its code. (Or can I?)
The entity is configured in 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();
});
I can create and query other tables. Only AppUser
table doesn't work in Oracle, it works fine using the SQL Server provider.
Tell me if more info about anything is needed.
Update
User class:
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 class:
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; }
}
The configuration of 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");
});
I just noticed a text is written in my output just before the exception:
fail: Microsoft.EntityFrameworkCore.Query[10100]
An exception occurred while iterating over the results of a query for context type 'Base.Security.Data.SecurityDbContext'.
You have included UserRoleOrganizations twice . Try to do including the good old way:
string username = "Administrator";
return _context.Users.Where(x => x.UserName.ToUpper() == username.ToUpper())
.Include(x => x.UserProfile)
.Include("UserRoleOrganizations.Organization");
.Include("UserRoleOrganizations.Role")
.FirstOrDefault();
But I don't like UserRoleOrganizations.Organization . Could you please show your User and UserRoleOrganizations clases?
Update.
Since I can see the classes, I think you can try to reverse query, maybe just for testing:
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;
And try to change the UserRoleOrganizations class:
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; }
....
}
User contributions licensed under CC BY-SA 3.0