【问题标题】:How to Exclude Fields from Navigation Property如何从导航属性中排除字段
【发布时间】:2021-11-18 06:08:26
【问题描述】:

我正在尝试使用 ASP.NET Core 5 来学习 .NET 5。这些天我正在研究一个使用 ASP.NET Core 5 MVC 和 NLayer 架构开发的博客项目。本项目使用 Entity Framework Core 5。

昨天,我决定只开发 Admin Dashboard 作为 MVC。对于前端,我会将数据呈现为 JSON 输出。但是,我遇到了有关此主题的问题。 JSON 输出有很多不必要的字段,如果某些字段对用户可见,则可能会很危险。例如,PasswordHash 字段。实体类有navigation property 并映射到DataAccess 层。我希望某些字段不会出现在 JSON 输出中。例如,Article 对象有一个User 字段。在这个User 字段中还有一个Articles 字段。我想从User 对象中提取PasswordHashArticles。我该怎么做?

下面带有 JSON 输出的图像包含太多不应出现在输出中的字段,以及太多重复数据。例如,在使用Django Rest Framework 进行开发时,我们可以使用exclude 来限制在serializer 中显示的字段。我怎样才能在.Net 5 项目中做到这一点?它在图像中不可见,但底部的类别对象仍然包含文章字段。

实体:

namespace Blog.Entities.Concrete
{
    public class Article : EntityBase, IEntity
    {
        public string Title { get; set; }
        public string Content { get; set; }
        public string Thumbnail { get; set; }
        public DateTime Date { get; set; }
        public int ViewsCount { get; set; } = 0;
        public int CommentCount { get; set; } = 0;
        public int CategoryId { get; set; }
        public Category Category { get; set; }
        public int UserId { get; set; }
        public User User { get; set; }
        public ICollection<Comment> Comments { get; set; }
        public string Slug { get; set; }

        // fields for seo
        public string SeoAuthor { get; set; }
        public string SeoDescription { get; set; }
        public string SeoTags { get; set; }
    }
}

业务层 - Manager 方法:

public async Task<IDataResult<ArticleListDto>> GetAll()
{
    var articles = await _unitOfWork.Articles
                                    .GetAllAsync(null, a => a.User, a => a.Category);

    if (articles.Count > -1)
    {
        return new DataResult<ArticleListDto>(ResultStatus.Success, new ArticleListDto()
                {
                    Articles = articles,
                    ResultStatus = ResultStatus.Success
                });
    }

    return new DataResult<ArticleListDto>(ResultStatus.Error, Messages.ArticleMessages.ArticleNotFound, null);
}

DataAccess - 实体框架核心 -> ArticleMap:

namespace Blog.DataAccess.Concrete.EntityFramework.Mappings
{
    public class ArticleMap : IEntityTypeConfiguration<Article>
    {
        public void Configure(EntityTypeBuilder<Article> builder)
        {
            builder.HasKey(a => a.Id);
            builder.Property(a => a.Id).ValueGeneratedOnAdd();
            builder.Property(a => a.Title).HasMaxLength(150);
            builder.Property(a => a.Title).IsRequired();
            builder.Property(a => a.Content).IsRequired();
            builder.Property(a => a.Content).HasColumnType("NVARCHAR(MAX)");
            builder.Property(a => a.Date).IsRequired();
            builder.Property(a => a.SeoAuthor).IsRequired();
            builder.Property(a => a.SeoAuthor).HasMaxLength(50);
            builder.Property(a => a.SeoDescription).HasMaxLength(200);
            builder.Property(a => a.SeoDescription).IsRequired();
            builder.Property(a => a.SeoTags).IsRequired();
            builder.Property(a => a.SeoTags).HasMaxLength(70);
            builder.Property(a => a.ViewsCount).IsRequired();
            builder.Property(a => a.CommentCount).IsRequired();
            builder.Property(a => a.Thumbnail).IsRequired();
            builder.Property(a => a.Thumbnail).HasMaxLength(250);
            builder.Property(a => a.Slug).IsRequired();
            builder.Property(a => a.Slug).HasMaxLength(200);
            builder.Property(a => a.CreatedByName).IsRequired();
            builder.Property(a => a.ModifiedByName).IsRequired();
            builder.Property(a => a.CreatedDate).IsRequired();
            builder.Property(a => a.ModifiedDate).IsRequired();
            builder.Property(a => a.IsActive).IsRequired();
            builder.Property(a => a.IsDeleted).IsRequired();
            builder.Property(a => a.Note).HasMaxLength(500);

            builder.HasOne<Category>(a => a.Category).WithMany(c => c.Articles).HasForeignKey(a => a.CategoryId);
            builder.HasOne<User>(a => a.User).WithMany(u => u.Articles).HasForeignKey(a => a.UserId);
            builder.ToTable("Articles");
        }
    }
}

ArticleListDto:

namespace Blog.Entities.Dtos
{
    public class ArticleListDto : DtoGetBase
    {
        public IList<Article> Articles { get; set; }
    }
}

Startup.cs:

namespace Blog.MVC
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews().AddRazorRuntimeCompilation().AddNewtonsoftJson(opt => opt.SerializerSettings.ReferenceLoopHandling
                = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
            services.AddAutoMapper(typeof(Startup));
            services.LoadMyServices();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseStatusCodePages();
            }

            app.UseStaticFiles();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapAreaControllerRoute(
                    name: "Admin",
                    areaName: "Admin",
                    pattern: "Admin/{controller=Home}/{action=Index}/{id?}"
                );
                endpoints.MapDefaultControllerRoute();
                endpoints.MapControllers();
            });
        }
    }
}

控制器 - ArticleController(用于 JSON 输出):

namespace Blog.MVC.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ArticleController : ControllerBase
    {
        private IArticleService _article;

        public ArticleController(IArticleService article)
        {
            _article = article;
        }

        [HttpGet("getall")]
        public async Task<IActionResult> GetAll()
        {
            var result = await _article.GetAll();
            if (result.ResultStatus == ResultStatus.Success)
            {
                return Ok(result.Data);
            }

            return BadRequest(result);
        }
    }
}

【问题讨论】:

    标签: c# json asp.net-core-mvc asp.net-core-webapi .net-5


    【解决方案1】:
    public class ArticleListDto : DtoGetBase {
      public IList<Article> Articles { get; set; }
    

    您将完全填充的数据库实体发送到前端

    创建一个只有您想要的属性的新类,将其命名为其他名称,以便您可以区分它们 (ArticleDto),然后发送一个列表。

    看看 AutoMapper - 它是一个可以在您的 Article 和 ArticleDto 之间来回转换的库,因此您不必费力地将每个属性值从 Article 中复制到 ArticleDto 中 - 刚刚注意到您似乎已经配置了 AutoMapper - 可能它在项目中的某个地方使用过; Ctrl-F for .Map 找到一个你可以(可能)复制的用法

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多