【问题标题】:How to get only few properties from related data in EF core?如何从 EF 核心中的相关数据中仅获取少数属性?
【发布时间】:2020-03-22 01:43:04
【问题描述】:

这是 ASP .net EF Core。

我有 2 节课。提名和用户具有一对多关系。

提名将具有指向用户 ID 的外键。

类:

public class Nomination
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int NominationId { get; set; }
    public int NomineeUserId { get; set; }
    public int NominationYear { get; set; }

    public User Nominee { get; set; }
}
public class User
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string EmailId { get; set; }
    public byte[] PasswordHash { get; set; }
    public byte[] PasswordSalt { get; set; }
}
public class UserDto
{
    public int UserId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

上下文:

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().HasKey(x => x.UserId);
        modelBuilder.Entity<User>().Property(x => x.UserId).ValueGeneratedOnAdd();

        modelBuilder.Entity<Nomination>().HasKey(x => x.NominationId);
        modelBuilder.Entity<Nomination>().Property(x => x.NominationId).ValueGeneratedOnAdd();
        modelBuilder.Entity<Nomination>().HasOne(x => x.Nominee).WithMany().HasForeignKey(x => x.NomineeUserId).OnDelete(DeleteBehavior.Restrict);
    }

控制器 GETALL:

public async Task<ActionResult<IEnumerable<Nomination>>> GetNomination()
    {
        return await _context.Nomination
            .Include(n => n.NominationDetails)
            .Include(n => n.Nominee)
            .ToListAsync();
    }

当我为 Namination 做 GETALL 时,我收到了“Nominee”(用户)的完整对象,包括密码内容。 我需要 UserDTO 而不是 User。我不需要用户的其他属性

我应该如何更改我的代码来实现这一点?

【问题讨论】:

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


    【解决方案1】:

    .ToListAsync() 之前使用 LINQ 的 .Select() 函数编写一个选择投影。

    这将创建一个 SQL 查询以仅获取您在 LINQ 中选择的那些 DB 列。

    例如提名Dto

    public class NominationDto
    {
        public int NominationId { get; set; }
        public int NomineeUserId { get; set; }
        public int NominationYear { get; set; }
        public UserDto Nominee { get; set; }
    }
    

    您的查询可能是:

    var nominations = await _context.Nomination
            .Include(n => n.NominationDetails)
            .Include(n => n.Nominee)
            .Select(nomination => new NominationDto
        {
            NominationId = nomination.NominationId,
            NomineeUserId = nomination.NomineeUserId,
            NominationYear = nomination.NominationYear,
            Nominee = new UserDto
            {
                UserId = nomination.Nominee.UserId,
                FirstName = nomination.Nominee.FirstName,
                LastName = nomination.Nominee.LastName
            }
        }).ToListAsync();
    

    请注意,您可能不需要 .Include(n =&gt; n.NominationDetails),因为在此查询中没有从 NominationDetails 获取/选择/投影。

    此外,如果您不打算跟踪这些实体的更改,那么为了获得更好的性能,您可以在 .Select().ToListAsync() 之前使用方法 .AsNoTracking() 告诉 EF 您不会跟踪检索到的更改EF 对象图中的数据。

    您可以在此处找到有关查询跟踪的更多信息:https://docs.microsoft.com/en-us/ef/core/querying/tracking

    【讨论】:

    • 感谢您的回答!我会尽快尝试并投票/标记答案正确。
    【解决方案2】:

    如果您没有使用 AutoMapper 等映射库,则可以使用 LINQ 选择并返回一个新类型,如下所示:

    假设我们也创建了一个NominationDto

    NominationDto.cs

    public class NominationDto
    {
        public int NominationId { get; set; }
        public int NomineeUserId { get; set; }
        public int NominationYear { get; set; }
        public UserDto Nominee { get; set; }
    }
    
    var nominations = _context.Nomination
        .Include(n => n.NominationDetails)
        .Include(n => n.Nominee)
        .Select(nomination => new NominationDto
        {
            NominationId = nomination.NominationId,
            NomineeUserId = nomination.NomineeUserId,
            NominationYear = nomination.NominationYear,
            Nominee = new UserDto
            {
                UserId = nomination.Nominee.UserId,
                FirstName = nomination.Nominee.FirstName,
                LastName = nomination.Nominee.LastName
            }
        }).ToListAsync();
    

    但是,如果可能的话,我建议使用AutoMapper 或其他映射库。它将处理将NominationUser 实体对象映射到NominationDtoUserDto 对象。这样你就不需要手工了。

    (我没有测试过上面的代码,但我相信这应该会让你处于一个很好的位置。)

    编辑 在对我的原始答案发表评论后,我更新了上面代码的 LINQ 部分。

    【讨论】:

    • 这将在针对数据库实现查询(包含所有相关数据)后导致内存投影。为了尽量减少对数据库的影响(将转到数据库的查询),应在.ToListAsync() 之前添加选择函数。
    • 糟糕!你是对的!谢谢你。我在写出来的时候考虑过这个问题,但不管出于什么原因,我决定把 Select 放在 ToListAsync() 之后。
    • 感谢您的回答!我会尽快尝试并标记答案正确。关于 AutoMapper。我创建了一个映射器文件,我可以使用 CreateMap。我将如何在 Controller 中继续这种方法?我应该在使用 _context 之前映射它吗?为愚蠢的问题道歉。 .net 新手。
    • @AthreyaMRBhatt 您将注入和 IMapper mapper 并将其分配给本地成员,例如_mapper 在 Controller 构造函数中,然后在 Action 方法中,您可以键入 _mapper.Map&lt;TDestination&gt;(sourceObject) 之类的东西。请阅读有关如何设置 AutoMapper 的说明:codementor.io/zedotech/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-12
    • 2015-04-24
    • 1970-01-01
    • 2010-12-16
    • 1970-01-01
    相关资源
    最近更新 更多