Entity Framework Core 8.0.10 不加载相关数据

问题描述 投票:0回答:1

我有用户和角色模型。两个模型之间生成多对多关系,由两端的集合表示:

ICollection<User> Users {get;set:} in the Role table
ICollection<Role> Roles {get;set:} in the User table

根据文档,默认情况下 EF 在 Sql Server DB 或其他引擎中生成中间表,其中包含两个表的外键。

当我尝试进行查询时,例如:

var user = await context.Users.Include(x=> x.Roles).SingleOrDefault(x=> x.UserId == 1);

它不会加载与用户关联的角色集合。我想澄清一下,用户 1 在数据库中分配了多个角色。

但是如果我运行以下代码:


var user = await context.Users
.AsNoTracking()
.Include(x=> x.Roles).SingleOrDefault(x=> x.UserId == 1);

角色集合确实带来了分配给用户的角色。我需要查询不是将其发送到视图,而是与从数据库检索的用户和角色数据进行交互,添加新角色,或删除其中之一,或编辑一些用户数据等。 我不知道是否有人有相同的经历,但是 EF 8.0.10 的这种行为对我来说似乎很奇怪。

如果有人能在这个问题上帮助我,我将非常感激。

我使用EntityFramework版本7.0.11,测试也不起作用。 我回到 8.0.10 版本并尝试显式加载集合:

await context.Entry ( user ).Collection ( x => x.Roles ).LoadAsync ();

它也不起作用。它不加载数据。

我只需要通过安装代理来尝试延迟加载,但这不是我需要的。

c# .net entity-framework-core many-to-many dbcontext
1个回答
0
投票

我相信我发现了问题。我正在开发的应用程序使用带有 DDD 的干净架构来丰富模型。对于包含多个属性的 User 实体,我将它们全部定义为 ObjectValues。例如,对于Id,我创建了一个名为UserId的类;对于 name 属性,我创建了一个名为 UserName 的类,等等。我对 Role 实体应用了相同的方法,为 Id 创建 RoleId 类,为角色名称创建 UserRole。

// 对象值/用户

public class UserId
{
    public Guid Value { get; }

    private UserId(Guid value) => Value = value;

    public static UserId Of(Guid value)
    {
        ArgumentNullException.ThrowIfNull(value);
        if (value == Guid.Empty)
        {
            throw new Exception("UserId cannot be empty");
        }
        return new UserId(value);
    }
}

public class UserName
{
    public string Value { get; }

    private UserName(string value) => Value = value;

    public static UserName Of(string value)
    {
        ArgumentNullException.ThrowIfNull(value);
        if (string.IsNullOrWhiteSpace(value))
        {
            throw new Exception("UserName cannot be empty");
        }
        return new UserName(value);
    }
}

// Object Values / Role
public class RoleId
{
    public Guid Value { get; }

    private RoleId(Guid value) => Value = value;

    public static RoleId Of(Guid value)
    {
        ArgumentNullException.ThrowIfNull(value);
        return new RoleId(value);
    }
}

public class RoleName
{
    public string Value { get; }

    private RoleName(string value) => Value = value;

    public static RoleName Of(string value)
    {
        ArgumentNullException.ThrowIfNull(value);
        if (string.IsNullOrWhiteSpace(value))
        {
            throw new Exception("RoleName cannot be empty");
        }
        return new RoleName(value);
    }
}

接下来,在每个用户和角色模型中,我创建了各自的导航属性。在 User 实体中,我创建了 Roles 属性,如下所示:

public List<Role> {get;set;}.

在角色实体中,我创建了用户导航属性:

public List<User> Users {get;set;}.

public class User : Entity<UserId>
{
    public UserName? UserName { get; set; }
    public List<Role> Roles { get; set; } = [];

    public User() { }

    public User(Guid id, string userName, string userEmail)
    {
        Id = UserId.Of(id);
        UserName = UserName.Of(userName);
    }

    public static User Create(Guid id, string userName)
    {
        return new User(id, userName);
    }
}

public class Role : Entity<RoleId>
{
    public RoleName RoleName { get; set; } = default!;
    public List<User> Users { get; } = [];
    public Role() { }

    public Role(Guid id, string roleName)
    {
        Id = RoleId.Of(id);
        RoleName = RoleName.Of(roleName);
    }

    public static Role Create(Guid id, string roleName)
    {
        return new Role(id, roleName);
    }
}

在数据库上下文 (SQL Server) 的 OnModelCreate 方法中,我定义了表名称、主键,并将 ObjectValue 属性转换为原始值,如下所示: 例如,对于 User 实体 Id,我这样做了:

builder.Property(u => u.Id).HasConversion(id => id.Value, value => new UserId(value));
。我将相同的逻辑应用于其余的用户属性以及角色实体。最后我将User和Role的关系定义为多对多,如下图:

// 用户

builder.ToTable("Users");
builder.HasKey(u => u.Id);
builder.Property(u => u.Id).HasConversion(id => id.Value, value => UserId.Of(value));
builder.Property(u => u.UserName).HasConversion(prop => prop!.Value, value => UserName.Of(value));

// Roles
builder.ToTable("Roles");
builder.HasKey(r => r.Id);
builder.Property(r => r.Id).HasConversion(id => id.Value, value => RoleId.Of(value));
builder.Property(r => r.RoleName).HasConversion(prop => prop!.Value, value => RoleName.Of(value));

// Many-to-Many Relationship
modelBuilder.Entity<User>()
    .HasMany(u => u.Roles)
    .WithMany(ur => ur.Users);

然后,通过控制器方法中注入的上下文,我尝试执行一个简单的查询来检索由 Id =“58c49479-ec65-4de2-86e7-033c546291aa”标识的用户及其分配的角色,如下所示:

var user = await _context.Users
    .Include(user => user.Roles)
    .Where(user => user.Id == UserId.Of("58c49479-ec65-4de2-86e7-033c546291aa"))
    .SingleOrDefaultAsync();

执行此查询时,它没有返回用户的关联角色(确实存在),并且生成了一个异常,表明查询返回了多个与过滤器匹配的记录,但事实并非如此,因为只有一个用户在数据库中具有单一角色。经过多次试验和错误后,我决定用原始值(在本例中为 Guid)替换用作用户和角色实体标识符的 ObjectValues。我在 OnModelCreate 中为用户和角色删除了 ObjectValue-to-Primitive 转换行,从而产生以下设置:

// 用户

builder.ToTable("Users");
builder.HasKey(u => u.Id);
builder.Property(u => u.UserName).HasConversion(prop => prop!.Value, value => new UserName(value));

// Roles
builder.ToTable("Roles");
builder.HasKey(r => r.Id);
builder.Property(r => r.RoleName).HasConversion(prop => prop!.Value, value => new RoleName(value));

// Many-to-Many Relationship
modelBuilder.Entity<User>()
    .HasMany(u => u.Roles)
    .WithMany(ur => ur.Users);

我还修改了用户和角色实体:

public class User : Entity<Guid>
{
    public UserName? UserName { get; set; }
    public List<Role> Roles { get; set; } = [];

    public User() { }

    public User(Guid id, string userName)
    {
        Id = id;
        UserName = UserName.Of(userName);
    }

    public static User Create(Guid id, string userName)
    {
        return new User(id, userName);
    }
}

public class Role : Entity<Guid>
{
    public RoleName RoleName { get; set; } = default!;
    public List<User> Users { get; } = [];
    public Role() { }

    public Role(Guid id, string roleName)
    {
        Id = id;
        RoleName = RoleName.Of(roleName);
    }

    public static Role Create(Guid id, string roleName)
    {
        return new Role(id, roleName);
    }
}

重新配置实体后,我再次运行查询,瞧!用户现在按预期返回关联的角色。我不确定 EFC 8 对于 ObjectValue 类型的实体标识符会发生什么,但它似乎不能很好地处理它们。目前,我更喜欢使用标识符的原始数据类型来避免这些问题。如果这可以帮助别人,那就太好了。或者,如果有人知道如何解决或解决这个问题,我很想听听。干杯!

© www.soinside.com 2019 - 2024. All rights reserved.