【转载】从头编写 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。

相关文章
相关标签/搜索