You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
405 lines
16 KiB
405 lines
16 KiB
3 months ago
|
using AutoMapper;
|
||
|
using AutoMapper.Internal;
|
||
|
using Kean.Application.Utilities.Interfaces;
|
||
|
using Kean.Infrastructure.Database;
|
||
|
using Kean.Infrastructure.Database.Repository.Default;
|
||
|
using Pluralize.NET;
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Data;
|
||
|
using System.Linq;
|
||
|
using System.Linq.Expressions;
|
||
|
using System.Reflection;
|
||
|
using System.Threading.Tasks;
|
||
|
|
||
|
namespace Kean.Application.Utilities.Implements
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// 增删改查通用服务实现
|
||
|
/// </summary>
|
||
|
public class CrudService : ICrudService
|
||
|
{
|
||
|
private readonly IMapper _mapper; // 模型映射
|
||
|
private readonly IDefaultDb _database; // 默认数据库
|
||
|
private readonly IPluralize _pluralizer; // 类型名转换器
|
||
|
|
||
|
private string _typeName; // 类型名
|
||
|
private TypeMap _typeMap; // 类型映射
|
||
|
private object _schema; // 数据库对象
|
||
|
private Type _schemaType; // 数据库对象类型
|
||
|
private MethodInfo _schemaFactory; // 数据库对象工厂
|
||
|
|
||
|
/// <summary>
|
||
|
/// 依赖注入
|
||
|
/// </summary>
|
||
|
public CrudService(
|
||
|
IMapper mapper,
|
||
|
IDefaultDb database,
|
||
|
IPluralize pluralizer)
|
||
|
{
|
||
|
_mapper = mapper;
|
||
|
_database = database;
|
||
|
_pluralizer = pluralizer;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 实现 Kean.Application.Utilities.Interfaces.ICrudService.GetCount 方法
|
||
|
*/
|
||
|
public Task<int> GetCount(string type, IDictionary<string, string> parameters)
|
||
|
{
|
||
|
CreateSchema(type);
|
||
|
if (parameters.Count > 0)
|
||
|
{
|
||
|
InvokeWhere(parameters);
|
||
|
}
|
||
|
return InvokeCount();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 实现 Kean.Application.Utilities.Interfaces.ICrudService.GetList 方法
|
||
|
*/
|
||
|
public Task<IEnumerable<object>> GetList(string type, IDictionary<string, string> parameters, string sort, int? offset, int? limit)
|
||
|
{
|
||
|
CreateSchema(type);
|
||
|
if (parameters.Count > 0)
|
||
|
{
|
||
|
InvokeWhere(parameters);
|
||
|
}
|
||
|
if (!string.IsNullOrEmpty(sort))
|
||
|
{
|
||
|
InvokeOrderBy(sort);
|
||
|
}
|
||
|
if (offset.HasValue)
|
||
|
{
|
||
|
InvokeSkip(offset.Value);
|
||
|
}
|
||
|
if (limit.HasValue)
|
||
|
{
|
||
|
InvokeTake(limit.Value);
|
||
|
}
|
||
|
return InvokeSelect();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 实现 Kean.Application.Utilities.Interfaces.ICrudService.GetItem 方法
|
||
|
*/
|
||
|
public Task<object> GetItem(string type, string id)
|
||
|
{
|
||
|
CreateSchema(type);
|
||
|
InvokeWhere(new Dictionary<string, string>()
|
||
|
{
|
||
|
[GetIdentifier().SourceMember.Name] = id
|
||
|
});
|
||
|
return InvokeSingle();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 实现 Kean.Application.Utilities.Interfaces.ICrudService.Create 方法
|
||
|
*/
|
||
|
public Task<object> Create(string type, IDictionary<string, string> values)
|
||
|
{
|
||
|
CreateSchema(type);
|
||
|
var entity = CreateEntity(values);
|
||
|
var timestamp = DateTime.Now;
|
||
|
_typeMap.DestinationType.GetProperty("CREATE_TIME")?.SetValue(entity, timestamp);
|
||
|
_typeMap.DestinationType.GetProperty("UPDATE_TIME")?.SetValue(entity, timestamp);
|
||
|
return InvokeAdd(entity);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 实现 Kean.Application.Utilities.Interfaces.ICrudService.Modify 方法
|
||
|
*/
|
||
|
public Task Modify(string type, string id, IDictionary<string, string> values)
|
||
|
{
|
||
|
CreateSchema(type);
|
||
|
var entity = CreateEntity(values, id);
|
||
|
_typeMap.DestinationType.GetProperty("UPDATE_TIME")?.SetValue(entity, DateTime.Now);
|
||
|
return InvokeUpdate(entity);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 实现 Kean.Application.Utilities.Interfaces.ICrudService.Delete 方法
|
||
|
*/
|
||
|
public Task Delete(string type, IEnumerable<string> id)
|
||
|
{
|
||
|
CreateSchema(type);
|
||
|
InvokeWhere(id);
|
||
|
return InvokeDelete();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 创建 Kean.Infrastructure.Database.ISchema 实例
|
||
|
*/
|
||
|
private void CreateSchema(string type)
|
||
|
{
|
||
|
if (_typeName?.Equals(type, StringComparison.OrdinalIgnoreCase) != true)
|
||
|
{
|
||
|
_typeName = type;
|
||
|
_typeMap = _mapper.GetMap(_pluralizer.Singularize(type));
|
||
|
_schemaFactory = _database.GetType().GetMethod("From", 1, Array.Empty<Type>()).MakeGenericMethod(_typeMap.DestinationType);
|
||
|
_schemaType = _schemaFactory.ReturnType;
|
||
|
}
|
||
|
_schema = _schemaFactory.Invoke(_database, null);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 获取标识属性
|
||
|
*/
|
||
|
private PropertyMap GetIdentifier()
|
||
|
{
|
||
|
return _typeMap.PropertyMaps.First(m => m.DestinationMember.GetCustomAttribute<IdentifierAttribute>() != null);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 创建实体
|
||
|
*/
|
||
|
private object CreateEntity(IDictionary<string, string> values, string id = null)
|
||
|
{
|
||
|
var entity = Activator.CreateInstance(_typeMap.DestinationType);
|
||
|
foreach (var item in _typeMap.PropertyMaps)
|
||
|
{
|
||
|
var property = item.DestinationMember as PropertyInfo;
|
||
|
if (id != null && property.GetCustomAttribute<IdentifierAttribute>() != null)
|
||
|
{
|
||
|
property.SetValue(entity, Convert.ChangeType(id, property.PropertyType));
|
||
|
continue;
|
||
|
}
|
||
|
var value = values.FirstOrDefault(i => item.SourceMember.Name.Equals(i.Key, StringComparison.OrdinalIgnoreCase)).Value;
|
||
|
if (value != null)
|
||
|
{
|
||
|
property.SetValue(entity, Convert.ChangeType(value, property.PropertyType));
|
||
|
}
|
||
|
}
|
||
|
return entity;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 Single 方法
|
||
|
*/
|
||
|
private Task<object> InvokeSingle()
|
||
|
{
|
||
|
var singleMethod = _schemaType.GetMethod(nameof(ISchema<IEntity>.Single), Array.Empty<Type>());
|
||
|
return (singleMethod.Invoke(_schema, null) as Task).ContinueWith(task =>
|
||
|
_mapper.Map(
|
||
|
task.GetType().GetProperty(nameof(Task<object>.Result)).GetValue(task),
|
||
|
_typeMap.DestinationType,
|
||
|
_typeMap.SourceType));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 Single 方法实现 Count
|
||
|
*/
|
||
|
private Task<int> InvokeCount()
|
||
|
{
|
||
|
var delegateType = typeof(Func<,>).MakeGenericType(_typeMap.DestinationType, typeof(object));
|
||
|
var singleMethod = _schemaType.GetMethod(nameof(ISchema<IEntity>.Single), new Type[] { typeof(Expression<>).MakeGenericType(delegateType) });
|
||
|
return (singleMethod.Invoke(_schema, new object[] { CreateCountExpression(delegateType) }) as Task).ContinueWith(task =>
|
||
|
{
|
||
|
var result = task.GetType().GetProperty(nameof(Task<object>.Result)).GetValue(task) as IDictionary<string, object>;
|
||
|
return Convert.ToInt32(result.Values.First());
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 Select 方法
|
||
|
*/
|
||
|
private Task<IEnumerable<object>> InvokeSelect()
|
||
|
{
|
||
|
var selectMethod = _schemaType.GetMethod(nameof(ISchema<IEntity>.Select), Array.Empty<Type>());
|
||
|
return (selectMethod.Invoke(_schema, null) as Task).ContinueWith(task =>
|
||
|
_mapper.Map(
|
||
|
task.GetType().GetProperty(nameof(Task<object>.Result)).GetValue(task),
|
||
|
typeof(IEnumerable<>).MakeGenericType(_typeMap.DestinationType),
|
||
|
typeof(IEnumerable<>).MakeGenericType(_typeMap.SourceType))
|
||
|
as IEnumerable<object>);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 Add 方法
|
||
|
*/
|
||
|
private Task<object> InvokeAdd(object entity)
|
||
|
{
|
||
|
var addMethod = _schemaType.GetMethod(nameof(ISchema<IEntity>.Add));
|
||
|
return (addMethod.Invoke(_schema, new object[] { entity }) as Task<object>).ContinueWith(task =>
|
||
|
{
|
||
|
_database.Save();
|
||
|
var identifier = _typeMap.PropertyMaps.SingleOrDefault(m => m.DestinationMember.GetCustomAttribute<IdentifierAttribute>()?.Increment == true);
|
||
|
if (identifier == null)
|
||
|
{
|
||
|
return task.Result;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return identifier.DestinationMember.GetMemberValue(entity);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 Update 方法
|
||
|
*/
|
||
|
private Task InvokeUpdate(object entity)
|
||
|
{
|
||
|
var updateMethod = _schemaType.GetMethod(nameof(ISchema<IEntity>.Update), new Type[] { _typeMap.DestinationType, typeof(string[]) });
|
||
|
return (updateMethod.Invoke(_schema, new object[] { entity, new string[] { "CREATE_TIME" } }) as Task).ContinueWith(task => _database.Save());
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 Delete 方法
|
||
|
*/
|
||
|
private Task InvokeDelete()
|
||
|
{
|
||
|
var deleteMethod = _schemaType.GetMethod(nameof(ISchema<IEntity>.Delete), Array.Empty<Type>());
|
||
|
return (deleteMethod.Invoke(_schema, null) as Task).ContinueWith(task => _database.Save());
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 Where 方法
|
||
|
*/
|
||
|
private void InvokeWhere(IDictionary<string, string> parameters)
|
||
|
{
|
||
|
var delegateType = typeof(Func<,>).MakeGenericType(_typeMap.DestinationType, typeof(bool?));
|
||
|
var whereMethod = _schemaType.GetMethod(nameof(ISchema<IEntity>.Where), new Type[] { typeof(Expression<>).MakeGenericType(delegateType) });
|
||
|
foreach (var item in parameters)
|
||
|
{
|
||
|
var expression = CreateConditionExpression(delegateType, item);
|
||
|
if (expression != null)
|
||
|
{
|
||
|
whereMethod.Invoke(_schema, new object[] { expression });
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 Where 方法
|
||
|
*/
|
||
|
private void InvokeWhere(IEnumerable<string> id)
|
||
|
{
|
||
|
var delegateType = typeof(Func<,>).MakeGenericType(_typeMap.DestinationType, typeof(bool?));
|
||
|
var whereMethod = _schemaType.GetMethod(nameof(ISchema<IEntity>.Where), new Type[] { typeof(Expression<>).MakeGenericType(delegateType) });
|
||
|
var expression = CreateBatchExpression(delegateType, id);
|
||
|
if (expression != null)
|
||
|
{
|
||
|
whereMethod.Invoke(_schema, new object[] { expression });
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 OrderBy 方法
|
||
|
*/
|
||
|
private void InvokeOrderBy(string sort)
|
||
|
{
|
||
|
var order = sort[0] == '~' ? Order.Descending : Order.Ascending;
|
||
|
var column = order == Order.Descending ? sort[1..] : sort;
|
||
|
var delegateType = typeof(Func<,>).MakeGenericType(_typeMap.DestinationType, typeof(object));
|
||
|
var expression = CreateColumnExpression(delegateType, column);
|
||
|
if (expression != null)
|
||
|
{
|
||
|
var orderByMethod = _schemaType.GetMethod(nameof(ISchema<IEntity>.OrderBy), new Type[] { typeof(Expression<>).MakeGenericType(delegateType), typeof(Order) });
|
||
|
orderByMethod.Invoke(_schema, new object[] { expression, order });
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 Skip 方法
|
||
|
*/
|
||
|
private void InvokeSkip(int offset)
|
||
|
{
|
||
|
_schemaType.GetMethod(nameof(ISchema<IEntity>.Skip))
|
||
|
.Invoke(_schema, new object[] { offset });
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 调用 Take 方法
|
||
|
*/
|
||
|
private void InvokeTake(int limit)
|
||
|
{
|
||
|
_schemaType.GetMethod(nameof(ISchema<IEntity>.Take))
|
||
|
.Invoke(_schema, new object[] { limit });
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 创建条件表达式
|
||
|
*/
|
||
|
private LambdaExpression CreateConditionExpression(Type delegateType, KeyValuePair<string, string> parameter)
|
||
|
{
|
||
|
var property = _typeMap.PropertyMaps
|
||
|
.FirstOrDefault(p => p.SourceMember.Name.Equals(parameter.Key, StringComparison.OrdinalIgnoreCase))?
|
||
|
.DestinationMember as PropertyInfo;
|
||
|
if (property != null)
|
||
|
{
|
||
|
var argument = Expression.Parameter(_typeMap.DestinationType, "a");
|
||
|
return Expression.Lambda(
|
||
|
delegateType,
|
||
|
Expression.Convert(
|
||
|
property.PropertyType == typeof(string) ?
|
||
|
Expression.Call(
|
||
|
Expression.Property(argument, property),
|
||
|
typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) }),
|
||
|
Expression.Constant(parameter.Value)) :
|
||
|
Expression.Equal(
|
||
|
Expression.Property(argument, property),
|
||
|
Expression.Constant(Convert.ChangeType(parameter.Value, property.PropertyType))),
|
||
|
typeof(bool?)),
|
||
|
argument);
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 创建字段表达式
|
||
|
*/
|
||
|
private LambdaExpression CreateColumnExpression(Type delegateType, string column)
|
||
|
{
|
||
|
var property = _typeMap.PropertyMaps
|
||
|
.FirstOrDefault(p => p.SourceMember.Name.Equals(column, StringComparison.OrdinalIgnoreCase))?
|
||
|
.DestinationMember as PropertyInfo;
|
||
|
if (property != null)
|
||
|
{
|
||
|
var argument = Expression.Parameter(_typeMap.DestinationType, "a");
|
||
|
return Expression.Lambda(
|
||
|
delegateType,
|
||
|
Expression.Convert(
|
||
|
Expression.Property(argument, property),
|
||
|
typeof(object)),
|
||
|
argument);
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 创建计数表达式
|
||
|
*/
|
||
|
private LambdaExpression CreateCountExpression(Type delegateType)
|
||
|
{
|
||
|
var property = _typeMap.DestinationType.GetProperties().FirstOrDefault(p => p.GetCustomAttribute<IdentifierAttribute>() != null);
|
||
|
var argument = Expression.Parameter(_typeMap.DestinationType, "a");
|
||
|
return Expression.Lambda(
|
||
|
delegateType,
|
||
|
Expression.Call(typeof(Function).GetMethod(nameof(Function.Count)),
|
||
|
Expression.Convert(
|
||
|
Expression.Property(argument, property ?? _typeMap.DestinationType.GetProperties().First()),
|
||
|
typeof(object))),
|
||
|
argument);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 创建批量表达式
|
||
|
*/
|
||
|
private LambdaExpression CreateBatchExpression(Type delegateType, IEnumerable<string> range)
|
||
|
{
|
||
|
var property = _typeMap.DestinationType.GetProperties().FirstOrDefault(p => p.GetCustomAttribute<IdentifierAttribute>() != null);
|
||
|
var argument = Expression.Parameter(_typeMap.DestinationType, "a");
|
||
|
return Expression.Lambda(
|
||
|
delegateType,
|
||
|
Expression.Convert(
|
||
|
Expression.Call(
|
||
|
typeof(Enumerable).GetMethod(nameof(Enumerable.Contains), new Type[] { typeof(IEnumerable<>).MakeGenericType(Type.MakeGenericMethodParameter(0)), Type.MakeGenericMethodParameter(0) }).MakeGenericMethod(property.PropertyType),
|
||
|
Expression.NewArrayInit(property.PropertyType, range.Select(i => Expression.Constant(Convert.ChangeType(i, property.PropertyType)))),
|
||
|
Expression.Property(argument, property)),
|
||
|
typeof(bool?)),
|
||
|
argument);
|
||
|
}
|
||
|
}
|
||
|
}
|