How to load navigation properties on an IdentityUser with UserManager - c#

I've extended IdentityUser to include a navigation property for the user's address, however when getting the user with UserManager.FindByEmailAsync, the navigation property isn't populated. Does ASP.NET Identity Core have some way to populate navigation properties like Entity Framework's Include(), or do I have to do it manually?
I've set up the navigation property like this:
public class MyUser : IdentityUser
{
public int? AddressId { get; set; }
[ForeignKey(nameof(AddressId))]
public virtual Address Address { get; set; }
}
public class Address
{
[Key]
public int Id { get; set; }
public string Street { get; set; }
public string Town { get; set; }
public string Country { get; set; }
}

Unfortunately, you have to either do it manually or create your own IUserStore<IdentityUser> where you load related data in the FindByEmailAsync method:
public class MyStore : IUserStore<IdentityUser>, // the rest of the interfaces
{
// ... implement the dozens of methods
public async Task<IdentityUser> FindByEmailAsync(string normalizedEmail, CancellationToken token)
{
return await context.Users
.Include(x => x.Address)
.SingleAsync(x => x.Email == normalizedEmail);
}
}
Of course, implementing the entire store just for this isn't the best option.
You can also query the store directly, though:
UserManager<IdentityUser> userManager; // DI injected
var user = await userManager.Users
.Include(x => x.Address)
.SingleAsync(x => x.NormalizedEmail == email);

The short answer: you can't. However, there's options:
Explicitly load the relation later:
await context.Entry(user).Reference(x => x.Address).LoadAsync();
This will require issuing an additional query of course, but you can continue to pull the user via UserManager.
Just use the context. You don't have to use UserManager. It just makes some things a little simpler. You can always fallback to querying directly via the context:
var user = context.Users.Include(x => x.Address).SingleOrDefaultAsync(x=> x.Id == User.Identity.GetUserId());
FWIW, you don't need virtual on your navigation property. That's for lazy-loading, which EF Core currently does not support. (Though, EF Core 2.1, currently in preview, will actually support lazy-loading.) Regardless, lazy-loading is a bad idea more often than not, so you should still stick to either eagerly or explicitly loading your relationships.

Update for .NET 6.0 with EF Core 6.0:
You can now configure the property to be automatically included on every query.
modelBuilder.Entity<MyUser>().Navigation(e => e.Address).AutoInclude();
For more info check out:
https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager#model-configuration-for-auto-including-navigations

I found it useful to write an extension on the UserManager class.
public static async Task<MyUser> FindByUserAsync(
this UserManager<MyUser> input,
ClaimsPrincipal user )
{
return await input.Users
.Include(x => x.InverseNavigationTable)
.SingleOrDefaultAsync(x => x.NormalizedUserName == user.Identity.Name.ToUpper());
}

Best Option in my case is to add a package reference to Microsoft.EntityFrameworkCore.Proxies and then in your services use the UseLazyLoadingProxies
.AddDbContext<YourDbContext>(
b => b.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString));
More infos
https://learn.microsoft.com/de-de/ef/core/querying/related-data/lazy

Related

Can't access entity framework relations outside Dbcontext when Lazyloading

I have a asp.net application using entity framework.
I have these two models:
public class CustomerModel
{
public int Id{get;set; }
[Required]
public string Name {get;set; }
[Required]
public string Surname { get; set; }
[Required]
[Range(18,110)]
public uint Age { get; set; }
[Required]
public virtual AdressModel Adress { get; set; }
[Required]
public DateTime Created { get; set; }
}
and
public class AdressModel
{
public int Id { get; set; }
[Required]
public int HouseNumber { get; set; }
[Required]
public string Town { get; set; }
[Required]
public string ZipCode { get; set; }
[Required]
public string Country { get; set; }
}
And a dbcontext class that looks like this:
public class DemoContext : DbContext
{
public DbSet<CustomerModel> Customers { get; set; }
public DbSet<AdressModel> Adresses { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseLazyLoadingProxies();
options.UseSqlite(#"Data Source=/home/ask/RiderProjects/Parkz/identifier.sqlite");
}
}
and then I have a controller that just needs to load all the customers that I have in my database, and their adresses.
For that purpose I have this:
public IActionResult sendhere()
{
List<CustomerModel> customers = new List<CustomerModel>();
using (var db = new DemoContext()) {
customers = db.Customers
.Include(c => c.Adress)
.ToList();
}
return Content("hi");
}
Which I have tried to debug a bit.
The issue is that as soon as I exit my "using" block, all the related adress objects only consist of this error:
System.InvalidOperationException: An error was generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.LazyLoadOnDisposedContextWarning': An attempt was made to lazy-load navigation 'CustomerModelProxy.Adress' after the associated DbContext was disposed. This exception can be suppressed or logged by passing event ID 'CoreEventId.LazyLoadOnDisposedContextWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
at Microsoft.EntityFrameworkCore.Diagnostics.EventDefinition`2.Log[TLoggerCategory](IDiagnosticsLogger`1 logger, TParam1 arg1, TParam2 arg2)
at Microsoft.EntityFrameworkCore.Diagnostics.CoreLoggerExtensions.LazyLoadOnDisposedContextWarning(IDiagnosticsLogger`1 diagnostics, DbContext context, Object entityType, String navigationName)
at Microsoft.EntityFrameworkCore.Infrastructure.Internal.LazyLoader.ShouldLoad(Object entity, String navigationName, NavigationEntry& navigationEntry)
at Microsoft.EntityFrameworkCore.Infrastruc…
Which is because I exited the context.
How do I still get to access the adresses of my customers, even though I exited the Dbcontext, so I can return them to a view
My general advice when working with EF is that entities shouldn't be referenced outside of the scope of their DbContext. You certainly can work with detached entities, but you have to respect that they are provided in an "as-is" state from the moment they leave the scope of the DbContext. For that reason I recommend that anything passed outside of the scope of the DbContext should be a POCO ViewModel or DTO class to avoid confusing whether an entity class is actually a functional, attached entity representing data domain state, or a detached shell.
Option 1: Deal with DTO/ViewModels.
public IActionResult sendhere()
{
using (var db = new DemoContext()) {
var customerDTOs = db.Customers
.Select(c => new CustomerDTO
{
// fill in the fields you want here.
Addresses = c.Addresses.Select(a => new AddressDTO
{
// fill in the address fields you want here.
}).ToList()
}).ToList();
return View(customerDTOs);
}
}
You can alternatively leverage Automapper, set up the desired projection rules and use ProjectTo<CustomerDTO>(config) to replace the use of Select() above.
When leveraging projection, you do not need lazy loading proxies at all. This has arguably become the defacto recommended approach for EF.
The advantages of the projection method are that these DTOs (or ViewModels) cannot be confused with being entities. The issue with detached entities is that where you have methods in your code that might accept entities, these methods might expect to get entities and access members that aren't loaded. If they are attached and within the scope of a DbContext, those members can be lazy-loaded (not ideal for performance reasons, but functional) however if they are detached you get errors or NullRefExceptions. The other advantage of projection is the payload of data being pulled from the database and sent to the view logic or end consists of just the data needed.
Option 2: Don't de-scope the DbContext. With projects like ASP.Net MVC web applications, you can leverage an IoC Container to provide dependency injection into your Controllers. In this way you can set up the DbContext to be injected into the constructor with a lifetime scope set to the Request. In this way, for any given request, all services/classes you might call can be managed by the container and have access to the DbContext.
public class SomeController
{
private readonly DemoContext _context;
public SomeController(DemoContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
}
public IActionResult sendhere()
{
var customers = _context.Customers
.Include(c => c.Address)
.ToList();
return View(customers);
}
}
This can be combined with Option 1 to avoid needing to scope a DbContext with each request/action and better facilitate situations where you may want to make multiple calls against the DbContext and ensure the same context instance is used. For IoC containers there are a number of different ones available, and I believe ASP.Net Core comes with a default one, though I personally use and recommend Autofac. It has good documentation and examples on how to wire it up with MVC projects.
Option 3: Eager load everything you're going to need to reference. The example you provided should actually work, but your real code is likely missing an eager-load (.Include()) for the desired relationship given your example doesn't attempt to do anything with the Customers collection you load.
If your code does:
List<CustomerModel> customers = new List<CustomerModel>();
using (var db = new DemoContext()) {
customers = db.Customers
.Include(c => c.Address)
.ToList();
}
var firstAddressId = customers.FirstOrDefault()?.Address.Id;
This should work as Addresses was eager loaded. However, if you had:
List<CustomerModel> customers = new List<CustomerModel>();
using (var db = new DemoContext()) {
customers = db.Customers
.ToList();
}
var firstAddressId = customers.FirstOrDefault()?.Address.Id;
... without the Include(c => c.Address), then it would fail with that error.
With EF Core if you are going to want to return entities outside of the scope of a DbContext and you have lazy loading proxies enabled, you will want to temporarily turn off the proxy creation to avoid proxy errors. In this case anything you don't eager load will be left #null or default.
List<CustomerModel> customers = new List<CustomerModel>();
using (var db = new DemoContext()) {
db.ContextOptions.ProxyCreationEnabled = false;
customers = db.Customers
.Include(c => c.Address)
.ToList();
}
Return View(customers);
This should ensure that EF doesn't use proxies for the queries in the scope of that DbContext instance which should be avoided whenever you want to pass entities outside of the scope of the DbContext. This can be useful when you know you won't need the overhead of eager loading every reference. However, it is much better to use projection (Option 1) in this case to avoid future confusion around whether entities might actually have #null data, or merely it wasn't eager loaded.

Custom IdentityUser class property/dependent entity is always null

I am using ASP.NET Core targeting NET6.0 using the Microsoft.AspNetCore.Identity user management system with EntityFrameworkCore.
I have a custom dependent entity (Players) which I want to link to my custom ApplicationUser class in a one-to-one relationship. Every AspNetUsers record has an associated single record in the previously existing my_db_schema.tblPlayers database table. The AspNetCore identity tables are in the same database. Both are populated with real data (This project is to migrate from AspNetIdentity to AspNetCore.Identity)
[Table("tblPlayers", Schema = "my_db_schema")]
public class Players
{
//[Key]
public int Id { get; set; }
public string PlayerName { get; set; }
//[ForeignKey("User")]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
}
The UserId is a foreign key to the Id column on the AspNetUsers table. The Id column is a primary key.
My custom ApplicationUser class is defined as this:
public class ApplicationUser : IdentityUser<string>
{
public Players Players { get; set; }
}
In my custom ApplicationDbContext OnModelCreating function, I have the following:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Players>().HasKey(t => t.Id);
modelBuilder.Entity<ApplicationUser>()
.HasOne<Players>(s => s.Players)
.WithOne(u => u.User)
.HasForeignKey<Players>(ad => ad.UserId).HasPrincipalKey<ApplicationUser>(x => x.Id).IsRequired();
}
I am not interested in any automatic removal/management of data in the tblPlayers database table. I am not interested in running migrations or scaffolding. All that I need is that for any place in my code where I get an instance of an ApplicationUser, that the Players property is populated with data from the tblPlayers database table. No matter what combination of fluid API, or annotations I use, the ApplicationUser.Players property is always null, and the rest of the ApplicationUser properties are populated as expected.
I have seen these articles:
Why custom properties for IdentityUser custom class are always null
Custom property is null IdentityUser
But both of these require a specific instantiation of the custom ApplicationUserManager class. I would like to avoid having to do that in all areas of my code where ApplicationUserManager is referenced if possible.
Please help :)
EDIT: I have implemented my own custom ApplicationUserManager already.
Implementing a custom UserManager is trivial -
public class CustomUserManager : UserManager<User>
Then register it at startup
services.AddIdentity<User, Role>()
.AddUserManager<QuestUserManager>()
From there you'll just inject the custom user manager class where you need it.
public class SomeController : Controller {
private readonly CustomUserManager _userManager;
public SomeController(CustomUserManager userManager)
{
_userManager = userManager;
}
}
The only way you'll get the Players property populated is to override the method(s) that return a User and add .Include(u => u.Players) to the EF Core queries - something like
public override async Task<User> FindByIdAsync(string userId)
return await Users.Include(u => u.Players).SingleOrDefaultAsync(u => u.Id == userId);
}
You don't have to create your own custom UserManager in order to do that. But #Mr.T is right, it is necessary to include the dependant table in order to retrieve records filled.
So you have 3 options, except creating a custom UserManager:
In case you are following the Repository pattern you would do something like the following:
//Yor 'UserRepository' or whatever
public async Task<List<ApplicationUser>> GetUsers()
{
var set = _context.Set<ApplicationUser>()
.Include(user => user.Players);
return await set.ToListAsync();
}
In case you are using the DbContext directly - you would basically do the same as within the method above.
And in case you are using the default UserManager:
var filledUsers = await _userManager.Users.Include(user => user.Players).ToListAsync();

Loading Original Values of Collection EF Core

I am using current Version of EF core and got trouble to load the original values.
I've the following constellation:
public class User
{
public string Name { get; set; }
public ICollection<UserRole> UserRoles { get; set; }
}
public class UserRole
{
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
Now I add a new UserRole to the User - something like that
user.UserRoles.Add(new UserRole() {Name="Admin", User=user}
After that I want to "revert" this step and tried to use Reload
dbContext.Entry(user).Reload();
But still there is the new Role added to the Collection.
I also tried
dbContext.Entry(user).Collection(x=>x.UserRole).Load();
But this also not work.
Is there something I am doing generally wrong?
Thanks for you help!
You can reset the context using
dbContext.ChangeTracker.Entries()ToList().ForEach(e => e.State = EntityState.Detached);. If you are using EF Core 5.0, you can use ChangeTracker.Clear().
Note: As #AluanHaddad says in one of the comments on the question, this will revert all unsaved changes. You can use a linq Where expression to limit which objects have their states reset:
dbContext.ChangeTracker.Entries()
.Where(e => e.Entity is UserRole)
.Where(e => e => e.State = EntityState.Added)
.ToList()
.ForEach(e => e.State = EntityState.Detached);
Also, you could consider implementing the Unit of work pattern so that there is less risk of having unrelated changes a that will be reverted.

Entity Framework 6.4 + PostgreSQL Relationship One-to-One Unilateral don't include entity [duplicate]

This question already has an answer here:
How can I query the foreign key objects using Entity Framework?
(1 answer)
Closed 2 years ago.
I have this code and relationships:
namespace Tequila.Models.DTOs
{
public class ApplicationContext : DbContext
{
public ApplicationContext(DbContextOptions options) : base(options)
{
}
public DbSet<Address> Address { get; set; }
public DbSet<User> User { get; set; }
}
[Table("address")]
public class Address
{
[Column("id")]
public long AddressId { get; set; }
[Column("user_id")]
[ForeignKey("User")]
public long UserId { get; set; }
public User User { get; set; }
}
[Table("user")]
public class User
{
[Key, Column("id")]
public long UserId { get; set; }
public Address Address { get; set; }
}
}
User -> id, name,...
Address -> id, address, user_id...
When I execute this route for find a user and your address, the SQL has generate is:
SELECT u.id, u.name, ... , u.password
FROM user AS u
WHERE u.id = #__Id_0
LIMIT 1
I've already tried many other ideas by documents official and community, but I don't have success.
I'm not understanding what is happening with this problem.
EDIT
I call the Include in context in my repository like this
User user = context.User.Include("Address").FirstOrDefault(u => u.Id == Id);
OR
User user = context.User.Include("Address").Where(u => u.Id == Id).FirstOrDefault();
And I create my OnModelCreating function
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<User>()
.HasOne(e => e.Address)
.WithOne()
.HasForeignKey("Address");
}
I tried many others configuration this function, but I still didn't succeed.
EDIT 2
I use this struture for to do this query of eager load. I tried to do another query with eager load too but with anothers entities and I had the same problem.
The address is not mandatory in user, but an user must is in address, in my case. So, I want to call an address when I call an user, and that user is on that address.
Setting only relationship (Foreign Key) via attributes not enough to get the related data on a query. For performance reason, as default behavior, EF doesn't return related objects as part of the query. You have to explicitly specify which related objects you want to return as part of your query.
Here below you can find sample code, so that while querying User object also gets Address details (the place where you created your query, you must have an instance of ApplicationContext class, I assume the name of the variable is dbContext):
dbContext.User.Include(u => u.Address).ToList();
The code above using ToList() method from Linq extension. If you use Microsoft.EntityFrameworkCore package, you can also write an async version of the above code like below:
dbContext.User.Include(u => u.Address).ToListAsync();
EDIT:
With 2 new Edits on your questions, I am editing my answer as well.
Firstly, update your OnModelCreating method as below, basically, we remove HasForeignKey method (as you have already defined this relationship using attribute on your class) and also explicitly specify WithOne method to have one-to-one relationship with User property of Address entity (on Microsoft Docs
you can find how it behaves if you do not pass parameter, shortly it says If the navigation property is to be used, then it must be specified.):
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<User>()
.HasOne(u => u.Address)
.WithOne(a => a.User);
}
Secondly, your query part as below should work:
var user = context.User.Include(u => u.Address).FirstOrDefault(u => u.Id == Id);

EF Core, Loads navigation property without Include() or Load() [duplicate]

I have some models like those below:
public class Mutant
{
public long Id { get; set; }
...
// Relations
public long OriginalCodeId { get; set; }
public virtual OriginalCode OriginalCode { get; set; }
public int DifficultyLevelId { get; set; }
public virtual DifficultyLevel DifficultyLevel { get; set; }
}
and
public class OriginalCode
{
public long Id { get; set; }
...
// Relations
public virtual List<Mutant> Mutants { get; set; }
public virtual List<OriginalCodeInputParameter> OriginalCodeInputParameters { get; set; }
}
and in the OnModelCreating of DBContext I made the relations like these:
modelBuilder.Entity<Mutant>()
.HasOne(m => m.OriginalCode)
.WithMany(oc => oc.Mutants)
.HasForeignKey(m => m.OriginalCodeId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
modelBuilder.Entity<Mutant>()
.HasOne(m => m.DifficultyLevel)
.WithMany(dl => dl.Mutants)
.HasForeignKey(m => m.DifficultyLevelId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
now when I request for Mutants, the OriginalCode is null:
but as soon as I request for OriginalCodes like below:
then the OriginalCode field of the mutants will be not null:
What is the reason and how could I fix it?
The reason is explained in the Loading Related Data section of the EF Core documentation.
The first behavior is because EF Core currently does not support lazy loading, so normally you'll get null for navigation properties until you specifically load them via eager or explicit loading. However, the Eager loading section contains the following:
Tip
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
which explains why the navigation property is not null in the second case.
Now, I'm not sure which of the two behaviors do you want to fix, so will try to address both.
The first behavior can be "fixed" by using one of the currently available methods for loading related data, for instance eager loading:
var mutants = db.Mutants.Include(m => m.OriginalCode).ToList();
The second behavior is "by design" and cannot be controlled. If you want to avoid it, make sure to use fresh new DbContext instance just for executing a single query to retrieve the data needed, or use no tracking query.
Update: Starting with v2.1, EF Core supports Lazy Loading. However it's not enabled by default, so in order to utilize it one should mark all navigation properties virtual, install Microsoft.EntityFrameworkCore.Proxies and enable it via UseLazyLoadingProxies call, or utilize Lazy-loading without proxies - both explained with examples in the EF Core documentation.
Using Package Manager Console install Microsoft.EntityFrameworkCore.Proxies
install-package Microsoft.EntityFrameworkCore.Proxies
And then in your Context class add .UseLazyLoadingProxies():
namespace SomeAPI.EFModels
{
public partial class SomeContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer(connectionString);
}
}
}
}

Categories

Resources