Web API 和 Entity Framework 具有一对多对多的关系

Web API and Entity Framework with a 1 to many to many relationship

我需要有关如何从一对多获取数据的帮助。我正在使用 .NET Core 3.1 Web API 和 Entity Framework.

Table 1 : 用户

ID Guid
UserName String

Table 2 : UsererAgency

UserId GUID
AgencyCode String

Table 3 机构

AgencyCode string
AgencyName String

路径是从 table User,列 IDUserAgency.UserID 和从 UserAgencyAgency 使用 AgencyCode列。

我想获取所有用户及其代理机构。所以我想 return 只是:

这是我用来从 entity framework 获取数据的 POCO。

用户

public User()
{
    public User()
    {
        UserAgency = new HashSet<UserAgency>();
    }

    [NotMapped]
    public GUID ID { get; set; }
       
    [NotMapped]
    public virtual ICollection<UserAgency> UserAgency { get; set; }
}

public class UserAgency
{
    [Key]
    public Guid UserId { get; set; }
    [Key]
    [ForeignKey("Agency")]
    public string AgencyCode { get; set; }
    [NotMapped]
    public virtual Agency Agency { get; set; }
    [NotMapped]
    public virtual User User { get; set; }
}
  
public partial class Agency
{
    public Agency()
    {
        Employee = new HashSet<Employee>();
        UserAgency = new HashSet<UserAgency>();
        User = new HashSet<ApplicationUser>();
    }

    public string AgencyCode { get; set; }
    publicstring AgencyName { get; set; }

    [NotMapped]
    public virtual ICollection<UserAgency> UserAgency { get; set; }

    [NotMapped]
    public virtual ICollection<User> User { get; set; }
} 
var members = await _unitOfWork.UserRepository.FindAllAsync(null, null, new List<string> {"UserAgency" });

它正在 return 正确地处理我的数据。返回我的所有用户(这是正确的)和所有 UserAgency 以及他们在 Useragency 下的所有 Agency。我想我不知道如何向下钻取或如何格式化我的 DTO。我会使用 AutoMapper 还是可以 select 我需要什么?

有了EntityFramework,你可以这样试试:

_unitOfWork.UserRepository.FindAllAsync().Include(i => i.UserAgency).ThenInclude(i => i.Agency).Select(i => new { i.ID, i.UserAgency.Agency.AgencyCode, i.UserAgency.Agency.AgencyName  
});

EF Core 中没有 _unitOfWorkUserRepositoryFindAllAsync。看起来您使用了“通用存储库”anti模式,该模式通常会破坏 EF 操作,禁用真正的 UoW 功能和 LINQ 查询。 Gunnar Peipman 的 No need for repositories and unit of work with Entity Framework Core 详细解释了为什么这是个坏主意。

DbContext 已经是一个多实体工作单元。 DbSet 已经是一个单一实体的 Repository。 EF Core 中不需要“通用存储库”,因为 EF 已经提供了更高级别的抽象。但是, 有意义的是特定 存储库,这些存储库解决了特定使用 cases/scenarios/bounded 上下文(如果您更喜欢 DDD 术语)。

在 EF Core 中,return 您只需要代理 ID 和代理商数据:

var query=from user in dbContext.Users
            from ua in user.UserAgency 
            select new {
                UseID = user.ID, 
                AgencyCode = ua.Agency.AgencyCode,
                AgencyName = ua.Agency.AgencyName };
var results=await query.ToListAsync();

匿名类型不能用作 return 类型。您可以 return 元组代替,但更简洁的解决方案是定义 record 并使用您想要的字段:

public record AgencyWithUser(Guid UserID,string AgencyCode,string AgencyName);

....
var query=from user in dbContext.Users
            from ua in user.UserAgency 
            select new AgencyWithUser(
                user.ID, 
                ua.Agency.AgencyCode,
                ua.Agency.AgencyName);
var results=await query.ToListAsync();

如果这是一个常见的操作,代码可以作为一个函数添加到您的 DbContext。这可能是一个好主意,即使调用不那么常见,也可以从更高级别的代码中隐藏查询:

public class MyAgenciesContext: DbContext
{

....

    public async Task<List<AgencyWithUser>> GetAgenciesWithUsersAsync()
    {
        var query=from user in this.Users
            from ua in user.UserAgency 
            select new AgencyWithUser(
                user.ID, 
                ua.Agency.AgencyCode,
                ua.Agency.AgencyName);
        var results=await query.ToListAsync();
    }
}

您还可以使用满足特定用例要求的方法创建一个专业化 存储库。此查询不会有任何不同:

public MyAgencyManagementRepository
{

    readonly MyContext _dbContext;

    public MyAgencyManagementRepository(MyContext dbContext)
    {
        _dbContext=dbContext;
    }

    public async Task<List<AgencyWithUser>> GetAgenciesWithUsersAsync()
    {
        var query=from user in _dbContext.Users
            from ua in user.UserAgency 
            select new AgencyWithUser(
                user.ID, 
                ua.Agency.AgencyCode,
                ua.Agency.AgencyName);
        var results=await query.ToListAsync();
    }

    public async Task<List<AgencyWithUser>> GetAgenciesForUserAsync(Guid userId)
    {
        var query=from user in _dbContext.Users
            where user.Id == useId
            from ua in user.UserAgency 
            select new AgencyWithUser(
                user.ID, 
                ua.Agency.AgencyCode,
                ua.Agency.AgencyName);
        var results=await query.ToListAsync();
    }
}

与将方法放入 DbContext 没有什么不同。存储库的 真正 好处来自封装特定场景所需的复杂操作。

例如:

  • 对于报告,您只需要查询函数,通常结合多个实体、聚合、复杂过滤。不需要事务、验证或并发管理。不过可能需要动态过滤。

  • 对于机构管理,您需要基本的 CRUD 操作,这些操作还可以处理到用户的映射。需要 验证,您的代码将 处理并发问题。

尽管这两种情况都可以使用相同的 DbContext,但它们有不同的需求,需要不同的代码并因不同的原因而更改。他们需要不同的单元测试,甚至可能由不同的开发人员开发。

在这种情况下,创建两个单独的专业化存储库是有意义的