C# | 框架初级学习笔记

182 阅读3分钟

1.基础框架

领域驱动设计(DDD)
Domain Driven Design:领域层、应用层、展现层、基础设施层 DDD架构.png

  • Domain.Shared:常量,枚举【类似utils】
  • Domain:实体,集合根,领域服务,值类型,仓储接口【类似于entities】
  • Application.Contracts:应用服务接口和应用层的数据传输对象 (DTO)【类似于service中Interface和dto】
  • Application:应用服务接口实现【类似于service中Interface实现】
  • EntityFrameworkCore:定义DbContext 并实现.Domain模块中定义的仓储接口【mybatis的orm映射,逆向工程将实体类转为sql脚本】
  • DbMigrator:数据库迁移【jdbc操作】
  • HttpApi:定义API控制器【Controller】 依赖关系.png

2.增删改查

在Application层实现Application.Contracts接口具体的业务(增删改查)知识点总结

2.1接口实现

namespace Demo.Books
{
    // 使用别人的模块
    using Volo.Abp.Application.Dtos;
    using Volo.Abp.Domain.Repositories;
    
    public class BookAppService : [实现上层基类] ,IBookAppService
    {
        // bookRepository具有mybatis直接与数据库操作的对象【sqlSessionFactory?】返回的是Book对象数据
        private readonly IRepository<Book, Guid> bookRepository;
        // BookAppService有参构造【初始化bean?】
        public BookAppService(IRepository<Book, Guid> _bookRepository)
        {
           bookRepository = _bookRepository;
        }

       // TODO CRUD
    }
}

2.2分页查询

.GetQueryableAsync()
AsyncExecuter.CountAsync(q) | AsyncExecuter.ToListAsync(q)
ObjectMapper.Map<List<A>,List<B>>(ListA)多表复杂结构需要映射
PagedResultDto<>() | ListResultDto<>()

// 分页查询【GetPageAsync()方法】:GetBookDto前端发送数据,BookDto是返回给前端数据
public async Task<PagedResultDto<BookDto>> GetPageAsync(GetBookDto input)
{
    // input校验 GetBookDto中使用注解 [Required]必传字段
    if (input.Sorting.IsNullOrWhiteSpace())
    {
        input.Sorting = "err";
    }

    // GetQueryableAsync()方法查询数据
    var query = await bookRepository.GetQueryableAsync();

    // where条件查询过滤【linq语法】
    query =  query.Where(x => x.BookId == input.BookId);
    
    // AsyncExecuter.CountAsync(q)方法统计查询数量
    var totalCount = await AsyncExecuter.CountAsync(query);

    if (totalCount <= 0)
    {
        return new PagedResultDto<BookDto>();
    }

    // orderby pageby过滤【排序+分页条件】
    query = query.OrderBy(input.Sorting).PageBy(input);

    // AsyncExecuter.ToListAsync(q)方法将数据库查询数据转List<Book>
    var result = await AsyncExecuter.ToListAsync(query);

    // ObjectMapper.Map<List<Book>, List<BookDto>>(r))
    // List<Book> 转 List<BookDto> 
    return new PagedResultDto<BookDto>(totalCount,
        ObjectMapper.Map<List<Book>, List<BookDto>>(result));
}

/* ObjectMapper.Map映射 */
using AutoMapper;
namespace Demo.Books
{
    public class BookAutoMapperProfile : Profile
    {
        public BookAutoMapperProfile()
        {
            CreateMap<Book, BookDto>();
            //  CreateMap<Book, BookDto>().ForMember(...);
            CreateMap<SaveBookDto, Book>();
        }
    }
}

2.3新增和修改

// 新增和修改【SaveAsync()方法】:SaveBookDto前端发送数据,BookDto是返回给前端数据
public async Task<BookDto> SaveAsync(SaveBookDto input)
{
    // TODO input校验
    
    if (!input.Id.HasValue)
    {
        // 新增
        return await CreateAsync(input);
    }
    
    // 修改
    return await UpdateAsync(input);
}

.GetAsync(i) | .InsertAsync(o) | .UpdateAsync(o)

// 新增
private async Task<BookDto> CreateAsync(SaveBookDto input)
{
    // new Book,实例化前端传过来的数据
    var book = new Book(
       GuidGenerator.Create(), 
       input.Name, 
       input.Author,
       input.Year
       input.Infomation);

    // 插入数据
    book = await bookRepository.InsertAsync(book);

    return ObjectMapper.Map<Book, BookDto>(book);
}
// 修改
private async Task<ChannelAccountDto> UpdateAsync(SaveBookDto input)
{
    // 获取符合id的数据
    var book = await bookRepository.GetAsync(input.Id.Value);
    
    // 将 SaveBookDto 转 Book
    book = ObjectMapper.Map(input, Book);

    // 修改数据
    var editBook = await bookRepository.UpdateAsync(book);

    return ObjectMapper.Map<Book, BookDto>(editBook);
}

2.4删除

.DeleteAsync(i)

public async Task DeleteAsync(Guid id)
{
    await bookRepository.DeleteAsync(id);
}

2.5测试

在HttpApi模块中的Controller中写restful风格
Swagger集成测试

using Demo.Books;
using Volo.Abp;
namespace Demo.Books.Controllers
{
    [Route("api/book")]
    [Area("book")]
    [RemoteService(Name = "book")]
    public class BookController: [基类控制器]
    {
        // 同理类似于需要注入Bean【这里的是Application.Contracts中的接口】
        private readonly IBookAppService bookAppService;
        public BookController(IBookAppService _bookAppService)
        {
            this.bookAppService = _bookAppService;
        }
        
        // GetPageAsync测试
        [HttpGet]
        public async Task<PagedResultDto<BookDto>> GetPageAsync(GetBookDto input)
        {
            return await bookAppService.GetPageAsync(input);
        }

        // SaveAsync测试
        [HttpPost]
        public async Task<BookDto> SaveAsync(SaveBookDto input)
        {
            return await bookAppService.SaveAsync(input);
        }

        // DeleteAsync测试
        [HttpGet]
        [Route("{id}")]
        public async Task<BookDto> DeleteAsync(Guid id)
        {
            return await bookAppService.DeleteAsync(id);
        }
        
    }
}

3.常用方法

3.1查询

其他的查询方式:

.GetListAsync()    
.GetListAsync(query)
.GetAsync()   //查一个
.FirstOrDefaultAsync(query)    // 查询条件符合第一条数据

一对多的查询方式:

// 一对多(对象中有对象)
.WithDetailsAsync(x => x.Books)
// EFCore中加:
b.HasMany(x => x.Books).WithOne().HasForeignKey(x => x.UserId);
// Domian中加:
/*User*/
...
public virtual ICollection<Book> Books { get; protected set; }
public virtual void AddBook(IGuidGenerator guidGenerator,Guid bookId,...)
{
    Books.Add(new Book(guidGenerator.Create(), bookId, ...));
}
/*Book*/
public class Book : Entity<Guid>
{
...
public virtual GUid UserId {get; protected set;};
...
}
// Where条件中:
x =>UserId.Contains(x.Id)
x.Books.Any(t => t.UserId == x.Id)  //Books中多个

3.2父子节点递归树

/// <summary>
/// 递归初始化树
/// </summary>
/// <param name="nodes">结果</param>
/// <param name="parentID">父ID</param>
/// <param name="sources">数据源</param>
private void InitTree(IList<ProductDto> nodes, Guid? parentID, IList<ProductDto> sources)
{
    //递归寻找子节点  
    var tempTree = sources.Where(item => item.ParentId == parentID).ToList();
    foreach (ProductDto row in tempTree)
    {
       var tempNode = new ProductDto()
        {
            ...
            Id = row.Id,
            ParentId = row.ParentId,
            Children = new List<ProductDto>()
        };
        nodes.Add(tempNode);
        InitTree(tempNode.Children, row.Id, sources);
    }
}

3.3技巧方法

//将字符串数组string[] a转化成以逗号分隔符的字符串  
temp = string.Join(',', input.temp)  

//将数组根据逗号分隔成数组  
x=>x.temp.Split(',')

//手动分页
var baseList = list.Skip((input.PageNumber - 1) * input.PageSize).Take(input.PageSize).ToList();

4.进阶学习

参考资料

abp官方文档
C#实现Tree,包含parentId和children
时间序列化