Appearance
ORM扩展
ORM扩展是对数据库操作的扩展,使不同的类和方法可以使用统一的事务。实现了服务间的低耦合、高内聚。ORM扩展默认使用ado.net实现,也可以使用特定的ORM框架。目前实现的ORM扩展有Entity Framework和Dapper,也可以自行扩展自己的喜欢的ORM框架。
使用ORM扩展
首先引入组件,通过nuget引用:
sh
dotnet add package AsDI.ORM.Extend --version 2.0.0使用ORM扩展执行简单的SQL
对于简单的、仅需要执行SQL的情况,当使用ORM扩展时,仅需要使用接口,并在接口上添加标识 [Repository] ,例如:
C#
[Repository]
public interface IUserRepository
{
}当需要执行某一个SQL时,只需要增加一个方法,并传入参数即可,例如:
C#
[Repository]
public interface IUserRepository
{
[NativeExecute("update sys_user set DeptName=@deptName where DepartmentId=@id", SqlType=SqlType.DML)]
void UpdateDeptName(string id, string deptName);
}
//在Service中,注入并调用此方法
[Service]
public class UserService: IUserService
{
private readonly IUserRepository repository;
public UserService(IUserRepository repository)
{
//注入Repository
this.repository = repository;
}
public void OnDepartmentUpdate([ParameterOf(0)] DepartmentDTO departmentDTO)
{
//调用方法
repository.UpdateDeptName(departmentDTO.Id, departmentDTO.DeptName);
}
}接口的实现
有时候,执行一个SQL可能无法满足需要,可能有比较复杂的逻辑,需要自定义实现。如上例,可以直接继承 IUserRepository 接口。但因为继承了接口,接口里的所有方法都要实现,如果不想实现,可以直接抛出 NotImplementedException,如下:
C#
[Repository]
public interface IUserRepository
{
[NativeExecute("update sys_user set DeptName=@deptName where DepartmentId=@id", SqlType = SqlType.DML)]
void UpdateDeptName(string id, string deptName);
List<UserEntity> GetList(string deptUser);
}
[Service]
public class UserRepository : IUserRepository
{
public void UpdateDeptName(string id, string deptName)
{
//直接使用NativeExecute的SQL,可以不用实现
throw new NotImplementedException();
}
public List<UserEntity> GetList(string deptUser)
{
//实现自己特有的逻辑
return Execute("select * from sys_user where DepartmentId=@deptUser", new { deptUser });
}
}但更推荐的方式是将需要实现的方法和不需要实现的方法分离,如扩展 IUserRepository,,如下:
C#
[Repository]
public interface IUserRepository : IUserRepositoryImpl
{
// 这里写一些不需要实现的接口方法
[NativeExecute("update sys_user set DeptName=@deptName where DepartmentId=@id", SqlType = SqlType.DML)]
void UpdateDeptName(string id, string deptName);
}
public interface IUserRepositoryImpl
{
// 这里写一些需要实现的接口方法
public List<UserEntity> GetList(string deptUser);
}
[Service]
public class UserRepositoryImpl : IUserRepositoryImpl
{
public List<UserEntity> GetList(string deptUser)
{
return Execute("select * from sys_user where DepartmentId=@deptUser", new { deptUser });
}
}在net8.0以上版本,接口可以直接实现,更推荐的是直接实现,而不需要扩展,例如:
C#
[Repository]
public interface IUserRepository
{
[NativeExecute("update sys_user set DeptName=@deptName where DepartmentId=@id", SqlType = SqlType.DML)]
void UpdateDeptName(string id, string deptName);
// 直接实现,不需要继承(仅net8.0及以上版本支持)
List<UserEntity> GetList(string deptUser)
{
return Execute("select * from sys_user where DepartmentId=@deptUser", new { deptUser });
}
}使用全局事务
当一个服务需要调用多个Repository时,可以使用全局事务。全局事务使得在这个服务里调用的所有的Repository的操作,都会在一个事务内执行,而仅需要在方法上标识 [AutoTrans] 即可,这个标识可以写在实现的方法上,也可以写在接口的方法上,如下:
C#
[Service]
public class ApiInfoService : IApiInfoService
{
private readonly IApiInfoRepository repository;
public ApiInfoService(IApiInfoRepository repository)
{
this.repository = repository;
}
[AutoTrans]
public override IEnumerable<ApiInfoDTO> Insert(IEnumerable<ApiInfoDTO> dtos)
{
repository.DeleteAll();
return base.Insert(dtos);
}
}AutoTrans有两个属性 RollBackFor 和 ContinueFor ,当仅需要对特定的异常进行回滚时,设置 RollBackFor 即可, 如果当某个异常不回滚时,设置 ContinueFor 即可。
自定义ORM扩展
如果想使用原来的ORM,仅需要只实现 IDbExecutor , 比如使用Dapper,可以实现如下:
C#
[Service(1)]
public class DapperExecutor : IDbExecutor
{
private static readonly AsyncLocal<IDbConnection> conn = new();
private static readonly AsyncLocal<IDbTransaction> transaction = new();
[Value("AsDI.Database.ConnectionString", Require = false)]
private string _connectionString;
[Value("AsDI.Database.Driver", Require = false)]
private string _providerName;
public IDbConnection Connection
{
get
{
if (conn.Value != null)
{
return conn.Value;
}
if (!string.IsNullOrEmpty(_providerName) && !string.IsNullOrEmpty(_connectionString))
{
var type = Type.GetType(_providerName);
if (type != null)
{
conn.Value = (IDbConnection)Activator.CreateInstance(type, _connectionString);
}
}
if (conn.Value == null)
{
conn.Value = AsDIContext.New<IDbConnection>();
}
if (conn.Value == null || TargetAnalyzer.FinalTarget(conn.Value) == null)
{
throw new ArgumentNullException("Connection can't intial , please check config");
}
return conn.Value;
}
}
public bool BeginTrans()
{
if (transaction.Value != null)
{
return false;
}
else
{
Connection.Open();
transaction.Value = Connection.BeginTransaction();
return true;
}
}
public void Commit()
{
try
{
transaction.Value?.Commit();
}
finally
{
if (conn.Value?.State != ConnectionState.Closed)
{
conn.Value?.Close();
}
conn.Value.Dispose();
conn.Value = null;
transaction.Value = null;
}
}
public void RollBack()
{
try
{
transaction.Value?.Rollback();
}
finally
{
if (conn.Value?.State != ConnectionState.Closed)
{
conn.Value?.Close();
}
conn.Value.Dispose();
conn.Value = null;
transaction.Value = null;
}
}
public void Dispose()
{
if (transaction.Value == null && conn.Value != null)
{
if (conn.Value?.State != ConnectionState.Closed)
{
conn.Value?.Close();
}
conn.Value.Dispose();
conn.Value = null;
transaction.Value = null;
}
}
public int Modify(string sql, IDictionary<string, object> ps)
{
return Connection.Execute(sql, ps, transaction.Value);
}
public object Query(string sql, IDictionary<string, object> parameters, Type returnType)
{
RawType rawType = RawType.From(returnType);
if (rawType.IsList)
{
var rtn = Connection.Query(rawType.ElementType, sql, parameters, transaction.Value);
var real = Activator.CreateInstance(returnType);
var addMethod = returnType.GetMethod("Add");
foreach (var v in rtn)
{
addMethod.Invoke(real, new object[] { v });
}
return real;
}
else if (rawType.IsArray)
{
var rtn = Connection.Query(rawType.ElementType, sql, parameters, transaction.Value);
var real = Array.CreateInstance(rawType.ElementType ?? typeof(object), rtn.Count());
int i = 0;
foreach (var v in rtn)
{
real.SetValue(v, i);
i++;
}
return real;
}
else
{
var value = Connection.QueryFirstOrDefault(returnType, sql, parameters, transaction.Value);
return value;
}
}
public T Query<T>(string sql, IDictionary<string, object> parameters)
{
return (T)Query(sql, parameters, typeof(T));
}
}当然Dapper已经上线,可以直接使用nuget引入:
sh
dotnet add package AsDI.ORM.Extend.Dapper --version 2.0.0