SummerBoot 1.3.3

There is a newer version of this package available.
See the version list below for details.
dotnet add package SummerBoot --version 1.3.3                
NuGet\Install-Package SummerBoot -Version 1.3.3                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SummerBoot" Version="1.3.3" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SummerBoot --version 1.3.3                
#r "nuget: SummerBoot, 1.3.3"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install SummerBoot as a Cake Addin
#addin nuget:?package=SummerBoot&version=1.3.3

// Install SummerBoot as a Cake Tool
#tool nuget:?package=SummerBoot&version=1.3.3                

<a href="https://jb.gg/OpenSourceSupport"> <img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png?_ga=2.140768178.1037783001.1644161957-503565267.1643800664&_gl=11rs8z57_gaNTAzNTY1MjY3LjE2NDM4MDA2NjQ._ga_V0XZL7QHEB*MTY0NDE2MTk1Ny4zLjEuMTY0NDE2NTE2Mi4w" width = "200" height = "200" alt="" align=center /></a>

SummerBoot的核心理念

将SpringBoot的先进理念与C#的简洁优雅合二为一,声明式编程,专注于”做什么”而不是”如何去做”。在更高层面写代码,更关心的是目标,而不是底层算法实现的过程,SummerBoot,致力于打造一个人性化框架,让.net开发变得更简单。

加入QQ群反馈建议

群号:799648362

Getting Started

Nuget

你可以运行以下命令在你的项目中安装 SummerBoot。

PM> Install-Package SummerBoot

支持框架

net core 3.1,net 5,net 6

说明

这是一个全声明式框架,用户只需要声明接口,框架会通过Reflection Emit技术,自动生成接口的实现类。

SummerBoot中的人性化的设计

1.如果我们想要在appsettings.json里配置web应用的ip和port该怎么办?1.在appsettings.json里直接写

{
    "urls":"http://localhost:7002;http://localhost:7012"
}

也可以使用summerboot增强的方式,在program.cs中,添加UseSummerBoot()

 public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>().UseSummerBoot();
                });

然后在appsettings.json里直接写

{
   "server": {
		"ip":"127.0.0.1",
       "port": 8048
     },
}
  1. AutoRegister注解,作用是让框架自动将接口和接口的实现类注册到IOC容器中,标注在实现类上,注解的参数为这个类对应的自定义接口的type和服务的生命周期ServiceLifetime(周期默认为scope级别),使用方式如下:
 public interface ITest
    {

    }

    [AutoRegister(typeof(ITest),ServiceLifetime.Transient)]
    public class Test:ITest
    {

    }
  1. ApiResult 接口返回值包装类,包含 code,msg和data,3个字段,让整个系统的返回值统一有序,有利于前端的统一拦截,统一操作。使用方式如下:
[HttpPost("CreateServerConfigAsync")]
public async Task<ApiResult<bool>> CreateServerConfigAsync(ServerConfigDto dto)
{
		var result = await serverConfigService.CreateServerConfigAsync(dto);
		return ApiResult<bool>.Ok(result);
}
  1. 对net core mvc的一些增强操作,包括全局错误拦截器,和接口参数校验失败后的处理,配合ApiResult,使得系统报错时,也能统一返回,使用方式如下,首先在startUp里注册该服务,注意,要放在mvc注册之后:
services.AddControllersWithViews();
services.AddSummerBootMvcExtension(it =>
{
		//是否启用全局错误处理
		it.UseGlobalExceptionHandle = true;
		//是否启用参数校验处理
		it.UseValidateParameterHandle = true;
});

4.1 全局错误拦截器使用后的效果 我们可以直接在业务代码里抛出错误,全局错误拦截器会捕捉到该错误,然后使用统一格式返回给前端,业务代码如下:

private void ValidateData(EnvConfigDto dto)
{
		if (dto == null)
		{
			throw new ArgumentNullException("参数不能为空");
		}
		if(dto.ServerConfigs==null|| dto.ServerConfigs.Count==0)
		{
			throw new ArgumentNullException("环境下没有配置服务器");
		}
}

如果业务代码里报错,则返回值如下:

{
  "code": 40000,
  "msg": "Value cannot be null. (Parameter '环境下没有配置服务器')",
  "data": null
}

4.2 接口参数校验失败后的处理的效果 我们在接口的参数dto里添加校验注解,代码如下

public class EnvConfigDto : BaseEntity
{
		/// <summary>
		/// 环境名
		/// </summary>
		[Required(AllowEmptyStrings = false, ErrorMessage = "环境名称不能为空")]
		public string Name { get; set; }
		/// <summary>
		/// 环境下对应的服务器
		/// </summary>
		[NotMapped]
		public List<int> ServerConfigs { get; set; }
}

如果参数校验不通过,则返回值如下:

{
  "code": 40000,
  "msg": "环境名称不能为空",
  "data": null
}
  1. QueryCondition,lambda查询条件组合,解决前端传条件过来进行过滤查询的痛点,除了基本的And和Or方法,还添加了更人性化的方法,一般前端传过来的dto里的属性,有字符串类型,如果他们有值则添加到查询条件里,所以特地提取了2个方法,包括了AndIfStringIsNotEmpty(如果字符串不为空则进行and操作,否则返回原表达式),OrIfStringIsNotEmpty(如果字符串不为空则进行or操作,否则返回原表达式), 同时dto里的属性,还有可能是nullable类型,即可空类型,比如 int? test代表用户是否填写某个过滤条件,如果hasValue则添加到查询条件里,所以特地提取了2个方法,AndIfNullableHasValue(如果可空值不为空则进行and操作,否则返回原表达式),OrIfNullableHasValue(如果可空值不为空则进行and操作,否则返回原表达式)用法如下:
//dto
public class ServerConfigPageDto : IPageable
{
	public int PageNumber { get; set; }
	public int PageSize { get; set; }
	/// <summary>
	/// ip地址
	/// </summary>
	public string Ip { get; set; }
	/// <summary>
	/// 连接名
	/// </summary>
	public string ConnectionName { get; set; }
	
	public int? Test { get; set; }
}
//condition
 var queryCondition = QueryCondition.True<ServerConfig>()
	.And(it => it.Active == 1)
	//如果字符串不为空则进行and操作,否则返回原表达式
	.AndIfStringIsNotEmpty(dto.Ip, it => it.Ip.Contains(dto.Ip))
	//如果可空值不为空则进行and操作,否则返回原表达式
	.AndIfNullableHasValue(dto.Test,it=>it.Test==dto.Test)
	.AndIfStringIsNotEmpty(dto.ConnectionName,it=>it.ConnectionName.Contains(dto.ConnectionName));
				
 var queryResult = await serverConfigRepository.Where(queryCondition)
	.Skip((dto.PageNumber - 1) * dto.PageSize).Take(dto.PageSize).ToPageAsync();

SummerBoot 如何操作数据库

底层基于dapper,上层通过模板模式,支持了常见的4种数据库类型(sqlserver,mysql,oracle,sqlite)的增删改查操作,如果有其他数据库需求,可以参考以上4个的源码,给本项目贡献代码,同时基于工作单元模式实现了事务。不支持多表的lambda查询,因为多表查询直接写sql更好更易理解。

准备工作

需要自己通过nuget安装相应的数据库依赖包,比如SqlServer的Microsoft.Data.SqlClient,mysql的Mysql.data, oracle的Oracle.ManagedDataAccess.Core

首先在startup.cs类中注册服务

services.AddSummerBoot();

services.AddSummerBootRepository(it =>
{
	//注册数据库类型,比如SqliteConnection,MySqlConnection,OracleConnection,SqlConnection
	it.DbConnectionType = typeof(SqliteConnection);
	//添加数据库连接字符串
	it.ConnectionString = "Data source=./mydb.db";
	//插入的时候自动添加创建时间,数据库实体类必须继承于BaseEntity,oracle继承于OracleBaseEntity
	it.AutoUpdateLastUpdateOn = true;
	//插入的时候自动添加创建时间,使用utc时间
	it.AutoUpdateLastUpdateOnUseUtc = false;
	//update的时候自动更新最后更新时间字段,数据库实体类必须继承于BaseEntity,oracle继承于OracleBaseEntity
	it.AutoAddCreateOn = true;
	//update的时候自动更新最后更新时间字段,使用utc时间
	it.AutoAddCreateOnUseUtc = false;
	//启用软删除,数据库实体类必须继承于BaseEntity,oracle继承于OracleBaseEntity
	it.IsUseSoftDelete = true;
});

定义一个数据库实体类

其中注解大部分来自于系统自带的命名空间System.ComponentModel.DataAnnotations 和 System.ComponentModel.DataAnnotations.Schema,比如表名Table,主键Key,主键自增DatabaseGenerated(DatabaseGeneratedOption.Identity),列名Column,不映射该字段NotMapped等,同时自定义了一部分注解,比如更新时忽略该列IgnoreWhenUpdateAttribute(主要用在创建时间这种在update的时候不需要更新的字段), 同时SummerBoot自带了一个基础实体类BaseEntity(oracle 为OracleBaseEntity),实体类里包括自增的id,创建人,创建时间,更新人,更新时间以及软删除标记,推荐实体类直接继承BaseEntity

public class Customer
{
    [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { set; get; }

    public string Name { set; get; }
    
    public int Age { set; get; } = 0;
    
    /// <summary>
    /// 会员号
    /// </summary>
    public string CustomerNo { set; get; }
    
    /// <summary>
    /// 总消费金额
    /// </summary>
    public decimal TotalConsumptionAmount { set; get; }
}

定义接口,并继承于IBaseRepository,同时添加AutoRepository注解表示让框架自动注册并生成实现类

[AutoRepository]
public interface ICustomerRepository : IBaseRepository<Customer>
{
}

增删改查操作,均支持异步同步

1. 查

通过IOC注入获得接口以后,就可以开始查了,支持正常查询与分页查询,查询有2种方式。

1.1 IQueryable链式语法查询。
//常规查询
var customers= customerRepository.Where(it => it.Age > 5).OrderBy(it => it.Id).Take(10).ToList();
//分页
var page2 = await customerRepository.Where(it => it.Age > 5).Skip(0).Take(10).ToPageAsync();
1.2 直接在接口里定义方法,并且在方法上加上注解Select,然后在Select里写sql语句
[AutoRepository]
public interface ICustomerRepository : IBaseRepository<Customer>
{
    //async
    [Select("select od.productName from customer c join orderHeader oh on c.id=oh.customerid" +
            " join orderDetail od on oh.id=od.OrderHeaderId where c.name=@name")]
    Task<List<CustomerBuyProduct>> QueryAllBuyProductByNameAsync(string name);

    [Select("select * from customer where age>@age order by id")]
    Task<Page<Customer>> GetCustomerByPageAsync(IPageable pageable, int age);

    //sync
    [Select("select od.productName from customer c join orderHeader oh on c.id=oh.customerid" +
            " join orderDetail od on oh.id=od.OrderHeaderId where c.name=@name")]
    List<CustomerBuyProduct> QueryAllBuyProductByName(string name);

    [Select("select * from customer where age>@age order by id")]
    Page<Customer> GetCustomerByPage(IPageable pageable, int age);

}

使用方法:

var result = await customerRepository.QueryAllBuyProductByNameAsync("testCustomer");

//page
var pageable = new Pageable(1, 10);
var page = customerRepository.GetCustomerByPage(pageable, 5);
1.3 注意:1.2查询里的分页支持,方法的返回值由Page这个类包裹,同时方法参数里必须包含 IPageable这个分页参数,sql语句里也要有order by,例如:
[Select("select * from customer where age>@age order by id")]
Page<Customer> GetCustomerByPage(IPageable pageable, int age);
1.4 接口同时自带了Get方法,通过id获取结果,和GetAll(),获取表里的所有结果集。

接口自带了Insert方法,可以插入单个实体,或者实体列表,如果实体类的主键名称为Id,且有Key注解,并且是自增的,那么插入后,框架会自动为实体的ID这个字段赋值,值为自增的ID值。
var customer = new Customer() { Name = "testCustomer" };
customerRepository.Insert(customer);

var customer2 = new Customer() { Name = "testCustomer2" };
var customer3 = new Customer() { Name = "testCustomer3" };
var customerList = new List<Customer>() { customer2, customer3 };
customerRepository.Insert(customerList);

1.1 接口自带了Delete方法,可以删除单个实体,或者实体列表
customerRepository.Delete(customer);

customerRepository.Delete(customerList);
1.2 同时还支持基于lambda表达式的删除,返回受影响的行数,例如
 var deleteCount = customerRepository.Delete(it => it.Age > 5);

1.1 接口自带了Update方法,可以更新单个实体,或者实体列表,联合主键的话,数据库实体类对应的多字段都添加key注解即可。
customerRepository.Update(customer);

customerRepository.Update(customerList);
1.2 同时还支持基于IQueryable链式语法的更新方式,返回受影响的行数,例如
var updateCount= customerRepository.Where(it=>it.Name == "testCustomer")
.SetValue(it=>it.Age,5)
.SetValue(it=>it.TotalConsumptionAmount,100)
.ExecuteUpdate();

事务支持

事务支持,需要在使用的时候注入自定义仓储接口的同时,也注入框架自带的IUnitOfWork接口,用法如下
//uow is IUnitOfWork interface
try
{
    uow.BeginTransaction();
    customerRepository.Insert(new Customer() { Name = "testCustomer2" });
    var orderDetail3 = new OrderDetail
    {
        OrderHeaderId = orderHeader.Id,
        ProductName = "ball",
        Quantity = 3
    };
    orderDetailRepository.Insert(orderDetail3);
    uow.Commit();
}
catch (Exception e)
{
    uow.RollBack();
}

如果有些特殊情况要求自己手写实现类怎么办?

1.1 定义一个接口继承于IBaseRepository,并且在接口中定义自己的方法,注意,此时该接口无需添加AutoRepository注解
public interface ICustomCustomerRepository : IBaseRepository<Customer>
{
    Task<List<Customer>> GetCustomersAsync(string name);

    Task<Customer> GetCustomerAsync(string name);

    Task<int> UpdateCustomerNameAsync(string oldName, string newName);

    Task<int> CustomQueryAsync();
}
1.2 添加一个实现类,继承于BaseRepository类和自定义的ICustomCustomerRepository接口,实现类添加AutoRegister注解,注解的参数为这个类对应的自定义接口的类型和服务的声明周期ServiceLifetime(周期默认为scope级别),添加AutoRegister注解的目的是让模块自动将自定义接口和自定义类注册到IOC容器中,后续直接注入使用即可,BaseRepository自带了Execute,QueryFirstOrDefault和QueryList方法,如果要接触更底层的dbConnection进行查询,参考下面的CustomQueryAsync方法,首先OpenDb(),然后查询,查询中一定要带上transaction:dbTransaction这个参数,查询结束以后CloseDb();
[ManualRepository(typeof(ICustomCustomerRepository))]
public class CustomCustomerRepository : BaseRepository<Customer>, ICustomCustomerRepository
{
    public CustomCustomerRepository(IUnitOfWork uow, IDbFactory dbFactory, RepositoryOption repositoryOption) : base(uow, dbFactory, repositoryOption)
    {
    }

    public async Task<Customer> GetCustomerAsync(string name)
    {
        var result =
            await this.QueryFirstOrDefaultAsync<Customer>("select * from customer where name=@name", new { name });
        return result;
    }

    public async Task<List<Customer>> GetCustomersAsync(string name)
    {
        var result = await this.QueryListAsync<Customer>("select * from customer where name=@name", new { name });

        return result;
    }

    public async Task<int> UpdateCustomerNameAsync(string oldName, string newName)
    {
        var result = await this.ExecuteAsync("update customer set name=@newName where name=@oldName", new { newName, oldName });
        return result;
    }

    public async Task<int> CustomQueryAsync()
    {
        this.OpenDb();
        var grid = await this.dbConnection.QueryMultipleAsync("select id from customer",transaction:dbTransaction);
        var id = grid.Read().FirstOrDefault()?.id;
        this.CloseDb();
        return id;
    }
}

SummerBoot如何操作http调用,我们使用Feign

在startup.cs类中注册服务

services.AddSummerBoot();

//添加feign请求拦截器
services.AddScoped<IRequestInterceptor,MyRequestInterceptor>();
services.AddSummerBootFeign();

编写接口,包括get,post(json方式),post(form表单形式),异常处理包括超时重试回退降级等

/// <summary>
    /// 会员接口
    /// </summary>
    [FeignClient(name: "CustomerService", url: "http://localhost:5001/", fallBack: typeof(CustomerFallBack))]
    [Polly(retry:3,timeout:2000,retryInterval:1000)]
    public interface ICustomerRepository
    {
        /// <summary>
        /// 更新会员总消费金额
        /// </summary>
        /// <param name="customerNo"></param>
        /// <param name="amount"></param>
        /// <returns></returns>
        [PostMapping("/Customer/UpdateCustomerAmount")]
        [Headers("Content-Type:application/json", "Accept:application/json", "Charset:utf-8")]
        Task<bool> UpdateCustomerAmount(string customerNo, decimal amount);
    }
	
 [FeignClient(name: "testFeign", url: "http://localhost:5001/")]
     [Polly(retry:3,timeout:2000,retryInterval:1000)]
     public interface IFeignExampleRepository
     {
         /// <summary>
         /// post(json方式)
         /// </summary>
         /// <param name="name"></param>
         /// <param name="dto"></param>
         /// <returns></returns>
         [PostMapping("/demo/TestJson")]
         [Headers("Content-Type:application/json", "Accept:application/json", "Charset:utf-8")]
         Task<int> TestJson(string name,[Body]AddOrderDto dto);
 
         /// <summary>
         /// post(form方式)
         /// </summary>
         /// <param name="dto"></param>
         /// <returns></returns>
         [PostMapping("/demo/TestForm")]
         Task<bool> TestForm([Form]FeignFormDto dto);
 
         /// <summary>
         /// get
         /// </summary>
         /// <param name="name"></param>
         /// <param name="age"></param>
         /// <returns></returns>
         [GetMapping("/demo/GetTest")]
         Task<DateTime> TestGet(string name,int age);
     }

更新记录

  • 2020-08-05 feign调用接口返回的状态码如果不是200,则抛出错误。feign里默认的client用的httpClient的超时时长改成3天。修复unitOfWork里callback方法里的bug。
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp3.1 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on SummerBoot:

Package Downloads
MiHome.Net

小米米家的原生c#sdk,他可以通过云端或者本地的方式用api来操作米家智能家居设备。 Xiaomi Mijia's native c# sdk can use APIs to operate Mijia smart home devices through the cloud or locally.

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on SummerBoot:

Repository Stars
TripleView/MiHome.Net
MiHome's C# native SDK,which can control Mijia smart devices through the network and local methods.
Version Downloads Last updated
2.1.5 129 8/30/2024
2.1.3 404 3/29/2024
2.1.2 100 3/27/2024
2.1.1 137 1/13/2024
2.1.0 122 1/12/2024
2.0.9 124 1/5/2024
2.0.8 163 12/21/2023
2.0.7 181 11/28/2023
2.0.6 161 11/3/2023
2.0.5 157 10/20/2023
2.0.4 244 7/8/2023
2.0.3 168 6/21/2023
2.0.2 172 5/25/2023
2.0.1 160 5/16/2023
2.0.0 265 4/7/2023
1.4.2 509 9/15/2022
1.4.1 457 7/28/2022
1.4.0 474 6/1/2022
1.3.9 419 5/20/2022
1.3.8 439 5/19/2022
1.3.7 457 5/6/2022
1.3.6 451 3/29/2022
1.3.5 457 3/25/2022
1.3.4 464 2/9/2022
1.3.3 446 2/8/2022
1.3.2 456 2/8/2022
1.3.1 518 1/30/2022 1.3.1 is deprecated because it has critical bugs.
1.3.0 520 1/14/2022 1.3.0 is deprecated because it has critical bugs.
1.2.0 7,176 4/24/2020 1.2.0 is deprecated because it has critical bugs.
1.1.1 759 3/18/2020 1.1.1 is deprecated because it has critical bugs.
1.0.1 866 1/1/2020 1.0.1 is deprecated because it has critical bugs.
1.0.0 1,282 9/23/2019 1.0.0 is deprecated because it has critical bugs.
0.1.0 755 1/1/2020 0.1.0 is deprecated because it has critical bugs.

将SpringBoot的先进理念与C#的简洁优雅合二为一,声明式编程,专注于”做什么”而不是”如何去做”。在更高层面写代码,更关心的是目标,而不是底层算法实现的过程,SummerBoot,致力于让.net开发变得更简单。