簡體   English   中英

在.NET Core下的分層體系結構中自動創建CRUD

[英]Automate CRUD creation in a layered architecture under .NET Core

我正在一個典型的三層架構下的新項目中工作:使用Angular作為前端的businessdataclient

在這個項目中,我們將有一個我們想要自動執行的重復任務:創建CRUD。 我們想要做的是生成模型和控制器(put,get,post,delete)以及來自實體及其屬性的其他基本項目信息。

我最好的選擇是什么? 我曾經考慮過模板T4,但是我對它們的無知使我懷疑它是否是最好的選擇。

例如,從這個實體:

public class User
{

    public int Id { get; set; }

    public string Name {get;set;}

    public string Email{ get; set; }

    public IEnumerable<Task> Task { get; set; }
}

我想生成以下模型:

public class UserModel
{

    public int Id { get; set; }

    public string Name {get;set;}

    public string Email{ get; set; }

    public IEnumerable<Task> Task { get; set; }
}

還有控制器:

{
    /// <summary>
    /// User controller
    /// </summary>
    [Route("api/[controller]")]
    public class UserController: Controller
    {
        private readonly LocalDBContext localDBContext;
        private UnitOfWork unitOfWork;

        /// <summary>
        /// Constructor
        /// </summary>
        public UserController(LocalDBContext localDBContext)
        {
            this.localDBContext = localDBContext;
            this.unitOfWork = new UnitOfWork(localDBContext);
        }

        /// <summary>
        /// Get user by Id
        /// </summary>
        [HttpGet("{id}")]
        [Produces("application/json", Type = typeof(UserModel))]
        public IActionResult GetById(int id)
        {
            var user = unitOfWork.UserRepository.GetById(id);
            if (user == null)
            {
                return NotFound();
            }

            var res = AutoMapper.Mapper.Map<UserModel>(user);
            return Ok(res);
        }

        /// <summary>
        /// Post an user
        /// </summary>
        [HttpPost]
        public IActionResult Post([FromBody]UserModel user)
        {
            Usuario u = AutoMapper.Mapper.Map<User>(user);
            var res = unitOfWork.UserRepository.Add(u);

            if (res?.Id > 0)
            {
                return Ok(res);
            }

            return BadRequest();

        }

        /// <summary>
        /// Edit an user
        /// </summary>
        [HttpPut]
        public IActionResult Put([FromBody]UserModel user)
        {
            if (unitOfWork.UserRepository.GetById(user.Id) == null)
            {
                return NotFound();
            }

            var u = AutoMapper.Mapper.Map<User>(user);

            var res = unitOfWork.UserRepository.Update(u);

            return Ok(res);

        }

        /// <summary>
        /// Delete an user
        /// </summary>
        [HttpDelete("{id}")]
        public IActionResult Delete(int id)
        {

            if (unitOfWork.UserRepository.GetById(id) == null)
            {
                return NotFound();
            }

            unitOfWork.UserRepository.Delete(id);

            return Ok();

        }

另外,我們需要添加AutoMapper映射:

public AutoMapper()
{
    CreateMap<UserModel, User>();
    CreateMap<User, UserModel>();
}

而UnitOfWork:

private GenericRepository<User> userRepository;

public GenericRepository<User> UserRepository
{
    get
    {

        if (this.userRepository== null)
        {
            this.userRepository= new GenericRepository<User>(context);
        }
        return userRepository;
    }
}

大多數結構都是相同的,除了一些必須手動完成的控制器的特定情況。

這是項目的簡化版本,您需要編寫該版本才能生成以前的代碼。 首先創建一個目錄,其中和任何未來的實體將去。 為簡單起見,我調用目錄Entities並創建了一個名為User.cs的文件,其中包含User類的源代碼。

對於每個模板,創建一個.tt文件,以實體名稱開頭,后跟函數名稱。 因此,用戶模型的tt文件將被稱為UserModel.tt,您可以將模型模板放入其中。 對於用戶控制器,USerController.tt,您將控制器模板放入其中。 將只有自動化文件,用戶通用存儲庫將被稱為UserGenericRepository.tt(您已經猜到了)您放置了通用存儲庫模板

模型的模板

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
    var hostFile = this.Host.TemplateFile;
    var entityName = System.IO.Path.GetFileNameWithoutExtension(hostFile).Replace("Model","");
    var directoryName = System.IO.Path.GetDirectoryName(hostFile);
    var fileName = directoryName + "\\Entities\\" + entityName + ".cs";
#>
<#= System.IO.File.ReadAllText(fileName).Replace("public class " + entityName,"public class " + entityName + "Model") #>

我注意到源文件沒有名稱空間或使用,因此如果沒有將用法添加到User.cs文件,UserModel文件將無法編譯,但是文件確實按照規范生成

Controller的模板

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
    var hostFile = this.Host.TemplateFile;
    var entityName = System.IO.Path.GetFileNameWithoutExtension(hostFile).Replace("Controller","");
    var directoryName = System.IO.Path.GetDirectoryName(hostFile);
    var fileName = directoryName + "\\" + entityName + ".cs";
#>
/// <summary>
/// <#= entityName #> controller
/// </summary>
[Route("api/[controller]")]
public class <#= entityName #>Controller : Controller
{
    private readonly LocalDBContext localDBContext;
    private UnitOfWork unitOfWork;

    /// <summary>
    /// Constructor
    /// </summary>
    public <#= entityName #>Controller(LocalDBContext localDBContext)
    {
        this.localDBContext = localDBContext;
        this.unitOfWork = new UnitOfWork(localDBContext);
    }

    /// <summary>
    /// Get <#= Pascal(entityName) #> by Id
    /// </summary>
    [HttpGet("{id}")]
    [Produces("application/json", Type = typeof(<#= entityName #>Model))]
    public IActionResult GetById(int id)
    {
        var <#= Pascal(entityName) #> = unitOfWork.<#= entityName #>Repository.GetById(id);
        if (<#= Pascal(entityName) #> == null)
        {
            return NotFound();
        }

        var res = AutoMapper.Mapper.Map<<#= entityName #>Model>(<#= Pascal(entityName) #>);
        return Ok(res);
    }

    /// <summary>
    /// Post an <#= Pascal(entityName) #>
    /// </summary>
    [HttpPost]
    public IActionResult Post([FromBody]<#= entityName #>Model <#= Pascal(entityName) #>)
    {
        Usuario u = AutoMapper.Mapper.Map<<#= entityName #>>(<#= Pascal(entityName) #>);
        var res = unitOfWork.<#= entityName #>Repository.Add(u);

        if (res?.Id > 0)
        {
            return Ok(res);
        }

        return BadRequest();

    }

    /// <summary>
    /// Edit an <#= Pascal(entityName) #>
    /// </summary>
    [HttpPut]
    public IActionResult Put([FromBody]<#= entityName #>Model <#= Pascal(entityName) #>)
    {
        if (unitOfWork.<#= entityName #>Repository.GetById(<#= Pascal(entityName) #>.Id) == null)
        {
            return NotFound();
        }

        var u = AutoMapper.Mapper.Map<<#= entityName #>>(<#= Pascal(entityName) #>);

        var res = unitOfWork.<#= entityName #>Repository.Update(u);

        return Ok(res);

    }

    /// <summary>
    /// Delete an <#= Pascal(entityName) #>
    /// </summary>
    [HttpDelete("{id}")]
    public IActionResult Delete(int id)
    {

        if (unitOfWork.<#= entityName #>Repository.GetById(id) == null)
        {
            return NotFound();
        }

        unitOfWork.<#= entityName #>Repository.Delete(id);

        return Ok();

    }
}
<#+
    public string Pascal(string input)
    {
        return input.ToCharArray()[0].ToString() + input.Substring(1);
    }
#>

AutoMapper的模板

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
    var directoryName = System.IO.Path.GetDirectoryName(this.Host.TemplateFile) + "\\Entities";
    var files = System.IO.Directory.GetFiles(directoryName, "*.cs");
#>
public class AutoMapper
{
<#
foreach(var f in files) 
{
    var entityName = System.IO.Path.GetFileNameWithoutExtension(f);
#>
    CreateMap<<#= entityName #>Model, <#= entityName #>>();
    CreateMap<<#= entityName #>, <#= entityName #>Model>();
<#
}
#>}

這基本上遍歷Entities文件夾中的每個文件,並在Entity和Entity Model之間創建映射器

通用存儲庫的模板

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
    var hostFile = this.Host.TemplateFile;
    var entityName = System.IO.Path.GetFileNameWithoutExtension(hostFile).Replace("GenericRepository","");
    var directoryName = System.IO.Path.GetDirectoryName(hostFile);
    var fileName = directoryName + "\\" + entityName + ".cs";
#>
public class GenericRepository
{
    private GenericRepository<<#= entityName #>> <#= Pascal(entityName) #>Repository;

    public GenericRepository<<#= entityName #>> UserRepository
    {
        get
        {
            if (this.<#= Pascal(entityName) #>Repository == null)
            {
                this.<#= Pascal(entityName) #>Repository = new GenericRepository<<#= entityName #>>(context);
            }
            return <#= Pascal(entityName) #>Repository;
        }
    }
}<#+
    public string Pascal(string input)
    {
        return input.ToCharArray()[0].ToString() + input.Substring(1);
    }
#>

這可能有點偏離主題,而不是直接回答相關問題。

但為什么這樣解決你的問題呢?

為什么不簡單地創建一個基礎CRUD控制器。 為它提供與其數據模型計數器部件相關的通用模型。

因此,BI模型具有與DAL模型等相同的屬性。然后,您可以創建一個通過屬性名稱映射的通用轉換器。 或者在屬性上設置自定義屬性以映射到目標名稱。

然后你只需說,將一個表導入你的實體模型。 而且,所有層都可以一直訪問,因為所有轉換和CRUD都是通用的。

更好的是,如果您需要在CRUD操作上發生某些特定事件(例如特定表),您可以簡單地將控制器重載到特定的模型類型,並且presto您有一個明確定義的區域來編寫通用的例外代碼辦法?

我真的沒有用這個建議解決潛在的問題嗎?

假設您的db CRUD的基本控制器看起來像(偽代碼):

public TEntity Get<TContext>(Expression<Func<TEntity, bool>> predicate, TContext context) where TContext : DbContext
        {

            TEntity item = context.Set<TEntity>().FirstOrDefault(predicate);
            return item;
        }

        public List<TEntity> GetList<TContext>(Expression<Func<TEntity, bool>> predicate, TContext context) where TContext : DbContext
        {
            List<TEntity> item = context.Set<TEntity>().Where(predicate).ToList();
            return item;
        }

        public List<TEntity> GetAll<TContext>(TContext context) where TContext : DbContext
        {
            List<TEntity> item = context.Set<TEntity>().ToList();
            return item;
        }

        public TEntity Insert<TContext>(TEntity input, TContext context) where TContext : DbContext
        {
            context.Set<TEntity>().Add(input);
            context.SaveChanges();
            return input;
        }

        public TEntity UpSert<TContext>(TEntity input, Expression<Func<TEntity, bool>> predicate, TContext context) where TContext : DbContext
        {
            if (input == null)
                return null;

            TEntity existing = context.Set<TEntity>().FirstOrDefault(predicate);



            if (existing != null)
            {

                input.GetType().GetProperty("Id").SetValue(input, existing.GetType().GetProperty("Id").GetValue(existing));
                context.Entry(existing).CurrentValues.SetValues(input);

                context.SaveChanges();
            }
            else
            {
                RemoveNavigationProperties(input);
                context.Set<TEntity>().Add(input);
                context.SaveChanges();
                return input;
            }

            return existing;
        }

如果你使用三層架構,那么創建核心並添加Interface Repository這個`public partial interface IRepository,其中T:BaseEntity {

    T GetById(object id);


    void Insert(T entity);


    void Insert(IEnumerable<T> entities);


    void Update(T entity);


    void Update(IEnumerable<T> entities);


    void Delete(T entity);


    void Delete(IEnumerable<T> entities);

    IQueryable<T> Table { get; }

    IQueryable<T> TableNoTracking { get; }
}

public interface IDbContext
{

    IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity;


    int SaveChanges();


    IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters)
        where TEntity : BaseEntity, new();


    IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters);


    int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters);


    void Detach(object entity);


    bool ProxyCreationEnabled { get; set; }


    bool AutoDetectChangesEnabled { get; set; }

}`

這些接口可以在服務模塊中使用,例如public partial class BlogService : IBlogService{ private readonly IRepository<BlogPost> _blogPostRepository; private readonly IRepository<BlogComment> _blogCommentRepository;} public partial class BlogService : IBlogService{ private readonly IRepository<BlogPost> _blogPostRepository; private readonly IRepository<BlogComment> _blogCommentRepository;}這是基於DI的

謝謝

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM