山东雷驰
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

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);
}
}
}