【轉載】從頭編寫 asp.net core 2.0 web api 基礎框架 (5) EF CRUD 【轉載】從頭編寫 asp.net core 2.0 web api 基礎框架 (5) EF CRU

【轉載】從頭編寫 asp.net core 2.0 web api 基礎框架 (5) EF CRUD

 

Github源碼地址:https://github.com/solenovex/Building-asp.net-core-2-web-api-starter-template-from-scratchhtml

這是第一大部分的最後一小部分。要完成CRUD的操做。git

Repository Pattern

咱們能夠直接在Controller訪問DbContext,可是可能會有一些問題:github

1.相關的一些代碼處處重複,有可能在程序中不少地方我都會更新Product,那樣的話我可能就會在多個Action裏面寫一樣的代碼,而比較好的作法是隻在一個地方寫更新Product的代碼。web

2.處處寫重複代碼還會致使另一個問題,那就是容易出錯。數據庫

3.還有就是難以測試,若是想對Controller的Action進行單元測試,可是這些Action還包含着持久化相關的邏輯,這就很難的精確的找出究竟是邏輯出錯仍是持久化部分出錯了。api

因此若是能有一種方法能夠mock持久化相關的代碼,而後再測試,就會知道錯誤不是發生在持久化部分了,這就能夠用Repository Pattern了。安全

Repository Pattern是一種抽象,它減小了複雜性,目標是使代碼對repository的實現更安全,而且與持久化要無關app

其中持久化無關這點我要明確一下,有時候是指能夠隨意切換持久化的技術,但這實際上並非repository pattern的目的,其真正的目的是能夠爲repository挑選一個最好的持久化技術。例如:建立一個Product最好的方式多是使用entity framework,而查詢product最好的方式多是使用dapper,也有可能會調用外部服務,而對調用repository的消費者來講,它不關心這些具體的實現細節。框架

首先再創建一個Material entity,而後和Product作成多對一的關係:asp.net

複製代碼
複製代碼
namespace CoreBackend.Api.Entities
{
    public class Material
    {
        public int Id { get; set; }
        public int ProductId { get; set; }
        public string Name { get; set; }
        public Product Product { get; set; }
    }

    public class MaterialConfiguration : IEntityTypeConfiguration<Material>
    {
        public void Configure(EntityTypeBuilder<Material> builder)
        {
            builder.HasKey(x => x.Id);
            builder.Property(x => x.Name).IsRequired().HasMaxLength(50);
            builder.HasOne(x => x.Product).WithMany(x => x.Materials).HasForeignKey(x => x.ProductId)
                .OnDelete(DeleteBehavior.Cascade);
        }
    }
}
複製代碼
複製代碼

修改Product.cs:

複製代碼
複製代碼
namespace CoreBackend.Api.Entities
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public float Price { get; set; }
        public string Description { get; set; }
        public ICollection<Material> Materials { get; set; }
    }

    public class ProductConfiguration : IEntityTypeConfiguration<Product>
    {
        public void Configure(EntityTypeBuilder<Product> builder)
        {
            builder.HasKey(x => x.Id);
            builder.Property(x => x.Name).IsRequired().HasMaxLength(50);
            builder.Property(x => x.Price).HasColumnType("decimal(8,2)");
            builder.Property(x => x.Description).HasMaxLength(200);
        }
    }
}
複製代碼
複製代碼

而後別忘了在Context裏面註冊Material的Configuration並添加DbSet屬性:

複製代碼
複製代碼
namespace CoreBackend.Api.Entities
{
    public class MyContext : DbContext
    {
        public MyContext(DbContextOptions<MyContext> options)
            : base(options)
        {
            Database.Migrate();
        }

        public DbSet<Product> Products { get; set; }
        public DbSet<Material> Materials { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfiguration(new ProductConfiguration());
            modelBuilder.ApplyConfiguration(new MaterialConfiguration());
        }
    }
}
複製代碼
複製代碼

 

而後添加一個遷移 Add-Migration AddMaterial:

而後數據庫直接進行遷移操做了,無需再作update-database。

 

創建一個Repositories文件夾,添加一個IProductRepository:

複製代碼
複製代碼
namespace CoreBackend.Api.Repositories
{
    public interface IProductRepository
    {
        IEnumerable<Product> GetProducts();
        Product GetProduct(int productId, bool includeMaterials);
        IEnumerable<Material> GetMaterialsForProduct(int productId);
        Material GetMaterialForProduct(int productId, int materialId);
    }
}
複製代碼
複製代碼

這個是ProductRepository將要實現的接口,裏面定義了一些必要的方法:查詢Products,查詢單個Product,查詢Product的Materials和查詢Product下的一個Material。

其中相似GetProducts()這樣的方法返回類型仍是有爭議的,IQueryable<T>仍是IEnumerable<T>。

若是返回的是IQueryable,那麼調用repository的地方還能夠繼續構建IQueryable,例如在真正的查詢執行以前附加一個OrderBy或者Where方法。可是這樣作的話,也意味着你把持久化相關的代碼給泄露出去了,這看起來是違反了repository pattern的目的。

若是是IEnumerable,爲了返回各類各樣狀況的查詢結果,須要編寫幾十個上百個查詢方法,那也是至關繁瑣的,幾乎是不可能的。

目前看來,兩種返回方式都有人在用,因此根據狀況定吧。咱們的程序需求比較簡單,因此使用IEnumerable。

而後創建具體的實現類 ProductRepository:

複製代碼
複製代碼
namespace CoreBackend.Api.Repositories
{
    public class ProductRepository : IProductRepository
    {
        private readonly MyContext _myContext;

        public ProductRepository(MyContext myContext)
        {
            _myContext = myContext;
        }

        public IEnumerable<Product> GetProducts()
        {
            return _myContext.Products.OrderBy(x => x.Name).ToList();
        }

        public Product GetProduct(int productId, bool includeMaterials)
        {
            if (includeMaterials)
            {
                return _myContext.Products
                    .Include(x => x.Materials).FirstOrDefault(x => x.Id == productId);
            }
            return _myContext.Products.Find(productId);
        }

        public IEnumerable<Material> GetMaterialsForProduct(int productId)
        {
            return _myContext.Materials.Where(x => x.ProductId == productId).ToList();
        }

        public Material GetMaterialForProduct(int productId, int materialId)
        {
            return _myContext.Materials.FirstOrDefault(x => x.ProductId == productId && x.Id == materialId);
        }
    }
}
複製代碼
複製代碼

這裏面要包含吃就會的邏輯,因此咱們須要MyContext(也有可能須要其餘的Service)那就在Constructor裏面注入一個。重要的是調用的程序不關心這些細節。

這裏也是編寫額外的持久化邏輯的地方,好比說查詢以後作個排序之類的。

(具體的Entity Framework Core的方法請查閱EF Core官方文檔:https://docs.microsoft.com/en-us/ef/core/

GetProducts,查詢全部的產品並按照名稱排序並返回查詢結果。這裏注意必定要加上ToList(),它保證了對數據庫的查詢就在此時此刻發生。

GetProduct,查詢單個產品,判斷一下是否須要把產品下面的原料都一塊兒查詢出來,若是須要的話就使用Include這個extension method。查詢條件能夠放在FirstOrDefault()方法裏面。

GetMaterialsForProduct,查詢某個產品下全部的原料。

GetMaterialForProduct,查詢某個產品下的某種原料。

創建好Repository以後,須要在Startup裏面進行註冊:

複製代碼
複製代碼
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
#if DEBUG
            services.AddTransient<IMailService, LocalMailService>();
#else
            services.AddTransient<IMailService, CloudMailService>();
#endif
            var connectionString = Configuration["connectionStrings:productionInfoDbConnectionString"];
            services.AddDbContext<MyContext>(o => o.UseSqlServer(connectionString));

            services.AddScoped<IProductRepository, ProductRepository>();
        }
複製代碼
複製代碼

針對Repository,最好的生命週期是Scoped(每一個請求生成一個實例)。<>裏面前邊是它的合約接口,後邊是具體實現。

使用Repository

先爲ProductDto添加一個屬性:

複製代碼
複製代碼
namespace CoreBackend.Api.Dtos
{
    public class ProductDto
    {
        public ProductDto()
        {
            Materials = new List<MaterialDto>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public float Price { get; set; }
        public string Description { get; set; }
        public ICollection<MaterialDto> Materials { get; set; }

        public int MaterialCount => Materials.Count;
    }
}
複製代碼
複製代碼

就是返回該產品所用的原料個數。

再創建一個ProductWithoutMaterialDto:

複製代碼
複製代碼
namespace CoreBackend.Api.Dtos
{
    public class ProductWithoutMaterialDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public float Price { get; set; }
        public string Description { get; set; }
    }
}
複製代碼
複製代碼

這個Dto不帶原料相關的導航屬性。

而後修改controller。

如今咱們可使用ProductRepository替代原來的內存數據了,首先在ProductController裏面注入ProductRepository:

複製代碼
複製代碼
   public class ProductController : Controller
    {
        private readonly ILogger<ProductController> _logger;
        private readonly IMailService _mailService;
        private readonly IProductRepository _productRepository;

        public ProductController(
            ILogger<ProductController> logger,
            IMailService mailService,
            IProductRepository productRepository)
        {
            _logger = logger;
            _mailService = mailService;
            _productRepository = productRepository;
        }
複製代碼
複製代碼

1.修改GetProducts這個Action:

複製代碼
複製代碼
        [HttpGet]
        public IActionResult GetProducts()
        {
            var products = _productRepository.GetProducts();
            var results = new List<ProductWithoutMaterialDto>();
            foreach (var product in products)
            {
                results.Add(new ProductWithoutMaterialDto
                {
                    Id = product.Id,
                    Name = product.Name,
                    Price = product.Price,
                    Description = product.Description
                });
            }
            return Ok(results);
        }
複製代碼
複製代碼

注意,其中的Product類型是DbContext和repository操做的類型,而不是Action應該返回的類型,並且咱們的查詢結果是不帶Material的,因此須要把Product的list映射成ProductWithoutMaterialDto的list。

而後試試:

查詢的時候報錯,是由於Product的屬性Price,在fluentapi裏面設置的類型是decimal(8, 2),而Price的類型是float,那麼咱們把全部的Price的類型都改爲decimal:

複製代碼
複製代碼
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Description { get; set; }
        public ICollection<Material> Materials { get; set; }
    }

    public class ProductCreation
    {
        [Display(Name = "產品名稱")]
        [Required(ErrorMessage = "{0}是必填項")]
        [StringLength(10, MinimumLength = 2, ErrorMessage = "{0}的長度應該不小於{2}, 不大於{1}")]
        public string Name { get; set; }

        [Display(Name = "價格")]
        [Range(0, Double.MaxValue, ErrorMessage = "{0}的值必須大於{1}")]
        public decimal Price { get; set; }

        [Display(Name = "描述")]
        [MaxLength(100, ErrorMessage = "{0}的長度不能夠超過{1}")]
        public string Description { get; set; }
    }

public class ProductDto
    {
        public ProductDto()
        {
            Materials = new List<MaterialDto>();
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Description { get; set; }
        public ICollection<MaterialDto> Materials { get; set; }

        public int MaterialCount => Materials.Count;
    }

public class ProductModification
    {
        [Display(Name = "產品名稱")]
        [Required(ErrorMessage = "{0}是必填項")]
        [StringLength(10, MinimumLength = 2, ErrorMessage = "{0}的長度應該不小於{2}, 不大於{1}")]
        public string Name { get; set; }

        [Display(Name = "價格")]
        [Range(0, Double.MaxValue, ErrorMessage = "{0}的值必須大於{1}")]
        public decimal Price { get; set; }

        [Display(Name = "描述")]
        [MaxLength(100, ErrorMessage = "{0}的長度不能夠超過{1}")]
        public string Description { get; set; }
    }

public class ProductWithoutMaterialDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public string Description { get; set; }
    }
複製代碼
複製代碼

還有SeedData裏面和即將廢棄的ProductService:

  View Code

而後在運行試試:

結果正確。

而後修改GetProduct:

複製代碼
複製代碼
[Route("{id}", Name = "GetProduct")]
        public IActionResult GetProduct(int id, bool includeMaterial = false)
        {
            var product = _productRepository.GetProduct(id, includeMaterial);
            if (product == null)
            {
                return NotFound();
            }
            if (includeMaterial)
            {
                var productWithMaterialResult = new ProductDto
                {
                    Id = product.Id,
                    Name = product.Name,
                    Price = product.Price,
                    Description = product.Description
                };
                foreach (var material in product.Materials)
                {
                    productWithMaterialResult.Materials.Add(new MaterialDto
                    {
                        Id = material.Id,
                        Name = material.Name
                    });
                }
                return Ok(productWithMaterialResult);
            }

            var onlyProductResult = new ProductDto
            {
                Id = product.Id,
                Name = product.Name,
                Price = product.Price,
                Description = product.Description
            };
            return Ok(onlyProductResult);
        }
複製代碼
複製代碼

首先再添加一個參數includeMaterial表示是否帶着Material表的數據一塊兒查詢出來,該參數有一個默認值是false,就是請求的時候若是不帶這個參數,那麼這個參數的值就是false。

經過repository查詢以後把Product和Material分別映射成ProductDto和MaterialDot。

試試,首先不包含Material:

目前數據庫的Material表沒有數據,能夠手動添加幾個,也能夠把數據庫的Product數據刪了,改一下種子數據那部分代碼:

  View Code

而後再試試GetProduct帶有material的查詢:

其中inludeMaterail這個參數須要使用query string的方式,也就是在uri後邊加一個問號,問號後邊跟着參數名,而後是等號,而後是它的值。若是有多個query string的參數,那麼每組參數之間用&分開。

而後再修改一下MaterialController:

複製代碼
複製代碼
namespace CoreBackend.Api.Controllers
{
    [Route("api/product")] // 和主Model的Controller前綴同樣
    public class MaterialController : Controller
    {
        private readonly IProductRepository _productRepository;
        public MaterialController(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }

        [HttpGet("{productId}/materials")]
        public IActionResult GetMaterials(int productId)
        {
            var materials = _productRepository.GetMaterialsForProduct(productId);
            var results = materials.Select(material => new MaterialDto
                {
                    Id = material.Id,
                    Name = material.Name
                })
                .ToList();
            return Ok(results);
        }

        [HttpGet("{productId}/materials/{id}")]
        public IActionResult GetMaterial(int productId, int id)
        {
            var material = _productRepository.GetMaterialForProduct(productId, id);
            if (material == null)
            {
                return NotFound();
            }
            var result = new MaterialDto
            {
                Id = material.Id,
                Name = material.Name
            };
            return Ok(result);
        }
    }
}
複製代碼
複製代碼

注意GetMaterials方法內,咱們往productRepository的GetMaterialsForProduct傳進去一個productId,若是repository返回的是空list可能會有兩種狀況:1 product不存在,2 product存在,而它沒有下屬的material。若是是第一種狀況,那麼應該返回的是404 NotFound,而第二種action應該返回一個空list。因此咱們須要一個方法判斷product是否存在,因此打開ProductRepository,添加方法:

        public bool ProductExist(int productId)
        {
            return _myContext.Products.Any(x => x.Id == productId);
        }

並在pull up member(右鍵點擊方法代碼--重構裏面有)到接口裏面:

複製代碼
複製代碼
namespace CoreBackend.Api.Repositories
{
    public interface IProductRepository
    {
        IEnumerable<Product> GetProducts();
        Product GetProduct(int productId, bool includeMaterials);
        IEnumerable<Material> GetMaterialsForProduct(int productId);
        Material GetMaterialForProduct(int productId, int materialId);
        bool ProductExist(int productId);
    }
}
複製代碼
複製代碼

而後再改一下Controller:

複製代碼
複製代碼
namespace CoreBackend.Api.Controllers
{
    [Route("api/product")] // 和主Model的Controller前綴同樣
    public class MaterialController : Controller
    {
        private readonly IProductRepository _productRepository;
        public MaterialController(IProductRepository productRepository)
        {
            _productRepository = productRepository;
        }

        [HttpGet("{productId}/materials")]
        public IActionResult GetMaterials(int productId)
        {
            var product = _productRepository.ProductExist(productId);
            if (!product)
            {
                return NotFound();
            }
            var materials = _productRepository.GetMaterialsForProduct(productId);
            var results = materials.Select(material => new MaterialDto
                {
                    Id = material.Id,
                    Name = material.Name
                })
                .ToList();
            return Ok(results);
        }

        [HttpGet("{productId}/materials/{id}")]
        public IActionResult GetMaterial(int productId, int id)
        {
            var product = _productRepository.ProductExist(productId);
            if (!product)
            {
                return NotFound();
            }
            var material = _productRepository.GetMaterialForProduct(productId, id);
            if (material == null)
            {
                return NotFound();
            }
            var result = new MaterialDto
            {
                Id = material.Id,
                Name = material.Name
            };
            return Ok(result);
        }
    }
}
複製代碼
複製代碼

試試:

結果都沒有問題!!!

可是看看上面controller裏面的代碼,處處都是映射,這種手寫的映射很容易出錯,若是entity有幾十個屬性,而後在多個地方須要進行映射,那麼這麼寫實在太糟糕了。

因此須要使用一個映射的庫:

AutoMapper

 autoMapper是最主流的.net映射庫,因此咱們用它。

經過nuget安裝automapper:

安裝完以後,首先要配置automapper。咱們要告訴automapper哪些entity和dto之間有映射關係。這個配置應該只建立一次,而且在startup的時候進行初始化。

在Startup的Configure方法添加:

複製代碼
複製代碼
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,
            MyContext myContext)
        {
            loggerFactory.AddNLog();
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler();
            }
            myContext.EnsureSeedDataForContext();
            app.UseStatusCodePages();

            AutoMapper.Mapper.Initialize(cfg =>
            {
                cfg.CreateMap<Product, ProductWithoutMaterialDto>();
            });

            app.UseMvc();
        }
複製代碼
複製代碼

建立映射關係,咱們須要使用AutoMapper.Mapper.Initialize方法,其參數是一個Action,這個Action的參數是一個Mapping Configuration。

cfg.CreateMap<Product, ProductWithoutMaterialDto>(),意思就是建立一個從Product到ProductWIthoutMaterialDto的映射關係。

AutoMapper是基於約定的,原對象的屬性值會被映射到目標對象相同屬性名的屬性上。若是屬性不存在,那麼就忽略它。

偶爾咱們可能須要對AutoMapper的映射進行一些微調,可是對於大多數狀況來講,上面這一句話就夠用了。

如今能夠在controller裏面使用這個映射了。

打開controller首先改一下GetProducts:

複製代碼
複製代碼
        [HttpGet]
        public IActionResult GetProducts()
        {
            var products = _productRepository.GetProducts();
            var results = Mapper.Map<IEnumerable<ProductWithoutMaterialDto>>(products);
            return Ok(results);
        }
複製代碼
複製代碼

使用Mapper.Map進行映射,<T>其中T是目標類型,能夠是一個model也能夠是一個集合,括號裏面的參數是原對象們。

運行試試:

沒問題,結果和以前是同樣的。

而後針對GetProduct,首先再創建一對映射:

            AutoMapper.Mapper.Initialize(cfg =>
            {
                cfg.CreateMap<Product, ProductWithoutMaterialDto>();
                cfg.CreateMap<Product, ProductDto>();
            });

而後GetProduct:

複製代碼
複製代碼
[Route("{id}", Name = "GetProduct")]
        public IActionResult GetProduct(int id, bool includeMaterial = false)
        {
            var product = _productRepository.GetProduct(id, includeMaterial);
            if (product == null)
            {
                return NotFound();
            }
            if (includeMaterial)
            {
                var productWithMaterialResult = Mapper.Map<ProductDto>(product);
                return Ok(productWithMaterialResult);
            }
            var onlyProductResult = Mapper.Map<ProductWithoutMaterialDto>(product);
            return Ok(onlyProductResult);
        }
複製代碼
複製代碼

運行,查詢包含Material,報錯:

這是由於ProductDto裏面有一個屬性 ICollection<Material> Materials,automapper不知道應該怎麼去映射它,因此咱們須要再添加一對Material到MaterialDto的映射關係。

複製代碼
複製代碼
            AutoMapper.Mapper.Initialize(cfg =>
            {
                cfg.CreateMap<Product, ProductWithoutMaterialDto>();
                cfg.CreateMap<Product, ProductDto>();
                cfg.CreateMap<Material, MaterialDto>();
            });
複製代碼
複製代碼

運行:

沒問題。

而後把MaterailController裏面也改一下:

  View Code

運行一下都應該沒有什麼問題。

上面都是查詢的Actions。

下面開始作CUD的映射更改。

添加:

修改ProductRepository,添加如下方法:

複製代碼
複製代碼
        public void AddProduct(Product product)
        {
            _myContext.Products.Add(product);
        }

        public bool Save()
        {
            return _myContext.SaveChanges() >= 0;
        }
複製代碼
複製代碼

AddProduct會把傳進來的product添加到context的內存中(姑且這麼說),可是尚未更新到數據庫。

Save方法裏面是把context所追蹤的實體變化(CUD)更新到數據庫。

而後把這兩個方法提取到IProductRepository接口裏:

複製代碼
複製代碼
    public interface IProductRepository
    {
        IEnumerable<Product> GetProducts();
        Product GetProduct(int productId, bool includeMaterials);
        IEnumerable<Material> GetMaterialsForProduct(int productId);
        Material GetMaterialForProduct(int productId, int materialId);
        bool ProductExist(int productId);
        void AddProduct(Product product);
        bool Save();
    }
複製代碼
複製代碼

修改Controller的Post:

複製代碼
複製代碼
[HttpPost]
        public IActionResult Post([FromBody] ProductCreation product)
        {
            if (product == null)
            {
                return BadRequest();
            }

            if (product.Name == "產品")
            {
                ModelState.AddModelError("Name", "產品的名稱不能夠是'產品'二字");
            }

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            var newProduct = Mapper.Map<Product>(product);
            _productRepository.AddProduct(newProduct);
            if (!_productRepository.Save())
            {
                return StatusCode(500, "保存產品的時候出錯");
            }

            var dto = Mapper.Map<ProductWithoutMaterialDto>(newProduct);

            return CreatedAtRoute("GetProduct", new { id = dto.Id }, dto);
        }
複製代碼
複製代碼

注意別忘了要返回的是Dto。

運行:

沒問題。

Put

                cfg.CreateMap<ProductModification, Product>();
複製代碼
複製代碼
[HttpPut("{id}")]
        public IActionResult Put(int id, [FromBody] ProductModification productModificationDto)
        {
            if (productModificationDto == null)
            {
                return BadRequest();
            }

            if (productModificationDto.Name == "產品")
            {
                ModelState.AddModelError("Name", "產品的名稱不能夠是'產品'二字");
            }

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }
            var product = _productRepository.GetProduct(id);
            if (product == null)
            {
                return NotFound();
            }
            Mapper.Map(productModificationDto, product);
            if (!_productRepository.Save())
            {
                return StatusCode(500, "保存產品的時候出錯");
            }

            return NoContent();
        }
複製代碼
複製代碼

這裏咱們使用了Mapper.Map的另外一個overload的方法,它有兩個參數。這個方法會把第一個對象相應的值賦給第二個對象上。這時候product的state就變成了modified了。

而後保存便可。

試試:

Partial Update

cfg.CreateMap<Product, ProductModification>();
複製代碼
複製代碼
[HttpPatch("{id}")]
        public IActionResult Patch(int id, [FromBody] JsonPatchDocument<ProductModification> patchDoc)
        {
            if (patchDoc == null)
            {
                return BadRequest();
            }
            var productEntity = _productRepository.GetProduct(id);
            if (productEntity == null)
            {
                return NotFound();
            }
            var toPatch = Mapper.Map<ProductModification>(productEntity);
            patchDoc.ApplyTo(toPatch, ModelState);

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (toPatch.Name == "產品")
            {
                ModelState.AddModelError("Name", "產品的名稱不能夠是'產品'二字");
            }
            TryValidateModel(toPatch);
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            Mapper.Map(toPatch, productEntity);

            if (!_productRepository.Save())
            {
                return StatusCode(500, "更新的時候出錯");
            }

            return NoContent();
        }
複製代碼
複製代碼

試試:

沒問題。

Delete

只是替換成repository,不涉及mapping。

在Repository添加一個Delete方法:

        public void DeleteProduct(Product product)
        {
            _myContext.Products.Remove(product);
        }

提取到IProductRepository:

void DeleteProduct(Product product);

而後Controller:

複製代碼
複製代碼
[HttpDelete("{id}")]
        public IActionResult Delete(int id)
        {
            var model = _productRepository.GetProduct(id);
            if (model == null)
            {
                return NotFound();
            }
            _productRepository.DeleteProduct(model);
            if (!_productRepository.Save())
            {
                return StatusCode(500, "刪除的時候出錯");
            }
            _mailService.Send("Product Deleted",$"Id爲{id}的產品被刪除了");
            return NoContent();
        }
複製代碼
複製代碼

運行:

Ok。

第一大部分先寫到這。。。。。。。。。。。。

接下來幾天比較忙,而後我再編寫第二大部分。我會直接弄一個已經重構好的模板,簡單講一下,而後重點是Identity Server 4.

到目前爲止能夠進行CRUD操做了,接下來須要把項目重構一下,而後再簡單用一下Identity Server4。

相關文章
相關標籤/搜索