Entity Framework Core - Best way to access a collection on an entity?

后端 未结 2 926
[愿得一人]
[愿得一人] 2021-01-21 20:34

I have a simple ASP.NET Core 2.0 + Entity Framework Core project:

I have an entity called ApplicationUser, which extends Microsoft.AspNet.Identity.Ide

相关标签:
2条回答
  • 2021-01-21 21:13

    Adding an answer here for EF Core 2.1 (Identity 4+).

    This answer is based on Lazy Loading. For other ways, see the end of the post.

    Tseng made a comment on the question, and at the time it was for EF 2.1 preview. 2.1 is RTM now, and that same link he posted is really your answer, if you upgrade to EF Core 2.1.

    It's really easy to use too. Following the direction for Lazy Loading with Proxies on the same link Tseng mentioned, you basically do this (assuming you've got your models defined properly with 'virtual' as per the link says):

    1. Install the Nuget package Microsoft.EntityFrameworkCore.Proxies.
    2. In your .AddDbContext<...>(...) or in your OnConfiguring(), add a call to .UseLazyLoadingProxies() For example, in Startup.cs:

          services.AddDbContextPool<ApplicationDbContext>(options =>
                  options.UseMySql(Configuration.GetConnectionString("ApplicationDbContext"))
                         .UseLazyLoadingProxies()
      
          );
      

    Rebuild. That's it. There are other ways mentioned too besides using Proxies, but this is the simplest.

    To retrieve data using this:

    var user = await _userManager.GetUserAsync(ApplicationUser);
    var addresses = user.UserAddresses;
    

    with models similar to:

    public class ApplicationUser : IdentityUser<int>
    {
        public ApplicationUser()
        {
            UserAddresses = new List<UserAddress>();
        }
    
        public virtual ICollection<UserAddress> UserAddresses { get; set; }
    }
    
    public class UserAddress
    {
        public int UserAddressId { get; set; }
        public int Id { get; set; }
        ...
        public virtual ApplicationUser User { get; set; }
    }
    

    If you prefer to use Eager Loading or Explicit Loading, the same link has the details. I just do not go over them here.

    0 讨论(0)
  • 2021-01-21 21:28

    First of, if you want to access data from your database, you must use DBContext. Secondly, the only way you can still access the database from another object is if the object was of type IQueryable that was returned directly or indirectly from the database.

    For instance, the following would return IQueryable<User>:

    var users = _context.Users.Where(u => u == user);
    

    This means you can access the users variable and access the database something like this:

    var books = users.Select(u => u.Books);
    

    Notice the _userManager.GetUserAsync(User) function does not return an IQueryable. This means that the ApplicationUser user will only contain what it was meant to fetch (the user object). Simply using user.Books will not help it fetch data from the database. Moreover, if I am not mistaken, GetUserAsync gets its data from the current claims principal, not the database. Either way, when you attempt to fetch the user, entity framework will not automatically fetch Books as well. This is as Igor mentioned due to lazy loading. https://docs.microsoft.com/en-us/ef/core/querying/related-data

    As for how optimized your ways are, you can check the exact SQL query being run in your console. The above way I have shown will surely run a single query since ef core will put those lines together and understand exactly what query to run. A straightforward way to access the same data would be something like

    _context.Books.Where(b => b.User == user)
    

    If this is all you plan to use user for, it would have a little better performance to use _userManager.GetUserId(), since it would only get one value from your claims. Then you can use:

    _context.Books.Where(b => b.UserId == userId)
    
    0 讨论(0)
提交回复
热议问题