实体框架核心 - 访问实体集合的最佳方式?

时间:2018-04-04 21:09:02

标签: asp.net-core entity-framework-core

我有一个简单的ASP.NET Core 2.0 + Entity Framework Core项目:

我有一个名为ApplicationUser的实体,它扩展了Microsoft.AspNet.Identity.IdentityUser,而ApplicationUser有一个名为Books的另一个实体的集合。

但是,当我尝试从UserUser中检索书籍集合后,从UserManager中检索它:

ApplicationUser user = await _userManager.GetUserAsync(User);
return user.Books;

我得到一个空集合。

如果我改为:

ApplicationUser user = await _userManager.GetUserAsync(User);
return _context.Users.Include(d => d.Books).FirstOrDefault(u => u == user);

然后我得到了正确的图书集。

我怀疑这是因为我从ApplicationUser检索到的UserManager缺少正确的DbContext我的问题是,有更好的方法吗?就像以某种方式能够从用户那里获得集合而不必通过LINQ的DbContext?是否会有显着的性能提升?

最后,关于EF Core如何理解它如何在引擎盖下工作会有什么好读?

2 个答案:

答案 0 :(得分:1)

首先,如果要从数据库访问数据,则必须使用DBContext。其次,您仍然可以从另一个对象访问数据库的唯一方法是,对象是直接或间接从数据库返回的类型IQueryable

例如,以下内容将返回IQueryable<User>

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

这意味着您可以访问users变量并访问数据库,如下所示:

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

请注意_userManager.GetUserAsync(User)函数不会返回IQueryable。这意味着ApplicationUser user将仅包含其获取的内容(用户对象)。简单地使用user.Books将无法帮助它从数据库中获取数据。此外,如果我没有弄错的话,GetUserAsync从当前的声明主体而不是数据库中获取数据。无论哪种方式,当您尝试获取用户时,实体框架也不会自动获取Books。这是因为Igor提到由于延迟加载。 https://docs.microsoft.com/en-us/ef/core/querying/related-data

至于如何优化您的方式,您可以检查在控制台中运行的确切SQL查询。我已经展示的上述方法肯定会运行一个查询,因为ef核心会将这些行放在一起并准确理解要运行的查询。访问相同数据的简单方法类似于

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

如果您打算使用user,则使用_userManager.GetUserId()会有更好的性能,因为它只会从您的声明中获得一个值。然后你可以使用:

_context.Books.Where(b => b.UserId == userId)

答案 1 :(得分:0)

在此处为EF Core 2.1(Identity 4+)添加答案。

此答案基于延迟加载。有关其他方法,请参阅文章末尾。

Tseng对问题进行了评论,当时是针对EF 2.1预览版的。 2.1现在是RTM,如果您升级到EF Core 2.1,那么他发布的链接确实是您的答案。

它真的也很容易使用。按照提到的same link Tseng上的用代理进行延迟加载的指示,基本上可以做到这一点(假设您已按照链接所述正确地使用“虚拟”定义了模型):

  1. 安装Nuget软件包Microsoft.EntityFrameworkCore.Proxies
  2. 在您的.AddDbContext<...>(...)OnConfiguring()中,向.UseLazyLoadingProxies()添加呼叫 例如,在Startup.cs中:

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

重建。而已。除了使用代理外,还提到了其他方法,但这是最简单的。

使用以下方法检索数据:

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

具有类似的型号:

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; }
}

如果您喜欢使用急切加载或显式加载,则same link包含详细信息。我只是不在这里翻阅。