FreeSql.DynamicProxy
DynamicProxyMeta.cs
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Linq.Expressions;
namespace FreeSql
{
public clast DynamicProxyMeta
{
public Type SourceType { get; }
public ConstructorInfo[] SourceConstructors { get; }
public Dictionary SourceConstructorsMergeParametersLength { get; }
public MemberInfo[] MatchedMemberInfos { get; }
public DynamicProxyAttribute[] MatchedAttributes { get; }
private Type[] _matchedAttributesTypes;
public string ProxyCSharpCode { get; }
public string ProxyClastName { get; }
public astembly Proxyastembly { get; }
public Type ProxyType { get; }
internal DynamicProxyMeta(
Type sourceType, ConstructorInfo[] constructors,
MemberInfo[] matchedMemberInfos, DynamicProxyAttribute[] matchedAttributes,
string proxyCSharpCode, string proxyClastName, astembly proxyastembly, Type proxyType)
{
this.SourceType = sourceType;
this.SourceConstructors = constructors;
this.SourceConstructorsMergeParametersLength = constructors?.GroupBy(a => a.GetParameters().Length)
.Select(a => new KeyValuePair(a.Key, constructors.Where(b => b.GetParameters().Length == a.Key).ToArray()))
.ToDictionary(a => a.Key, a => a.Value);
this.MatchedMemberInfos = matchedMemberInfos;
this.MatchedAttributes = matchedAttributes;
_matchedAttributesTypes = matchedAttributes?.Select(a => a?.GetType()).ToArray();
this.ProxyCSharpCode = proxyCSharpCode;
this.ProxyClastName = proxyClastName;
this.Proxyastembly = proxyastembly;
this.ProxyType = proxyType;
}
public object CreateSourceInstance(object[] parameters)
{
if (parameters == null || parameters.Length == 0)
return Activator.CreateInstance(this.SourceType, true);
if (this.SourceConstructorsMergeParametersLength.TryGetValue(parameters.Length, out var ctors) == false)
throw new ArgumentException($"{this.SourceType.DisplayCsharp()} 没有定义长度 {parameters.Length} 的构造函数");
return Activator.CreateInstance(this.SourceType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameters);
}
public object CreateProxyInstance(object source)
{
if (this.ProxyType == null) return source;
var proxy = CreateInstanceDefault(this.ProxyType);
CopyData(this.SourceType, source, proxy);
return proxy;
}
public DynamicProxyAttribute CreateDynamicProxyAttribute(int index)
{
if (index < 0 || index > this.MatchedAttributes.Length)
throw new ArgumentException($"{nameof(index)} 参数错误,值范围 0 至 {this.MatchedAttributes.Length}");
var attribute = CreateInstanceDefault(_matchedAttributesTypes[index]) as DynamicProxyAttribute;
CopyData(_matchedAttributesTypes[index], this.MatchedAttributes[index], attribute);
return attribute;
}
public void SetDynamicProxyAttributePropertyValue(int index, object source, string propertyOrField, object value)
{
if (source == null) return;
if (index < 0 || index > this.MatchedAttributes.Length)
throw new ArgumentException($"{nameof(index)} 参数错误,值范围 0 至 {this.MatchedAttributes.Length}");
SetPropertyValue(_matchedAttributesTypes[index], source, propertyOrField, value);
}
public static object CreateInstanceDefault(Type type)
{
if (type == null) return null;
if (type == typeof(string)) return default(string);
if (type.IsArray) return Array.CreateInstance(type, 0);
var ctorParms = InternalGetTypeConstructor0OrFirst(type, true)?.GetParameters();
if (ctorParms == null || ctorParms.Any() == false) return Activator.CreateInstance(type, null);
return Activator.CreateInstance(type, ctorParms.Select(a => a.ParameterType.IsInterface || a.ParameterType.IsAbstract || a.ParameterType == typeof(string) ? null : Activator.CreateInstance(a.ParameterType, null)).ToArray());
}
internal static NewExpression InternalNewExpression(Type that)
{
var ctor = InternalGetTypeConstructor0OrFirst(that);
return Expression.New(ctor, ctor.GetParameters().Select(a => Expression.Constant(CreateInstanceDefault(a.ParameterType), a.ParameterType)));
}
static ConcurrentDictionary _dicInternalGetTypeConstructor0OrFirst = new ConcurrentDictionary();
internal static ConstructorInfo InternalGetTypeConstructor0OrFirst(Type that, bool isThrow = true)
{
var ret = _dicInternalGetTypeConstructor0OrFirst.GetOrAdd(that, tp =>
tp.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[0], null) ??
tp.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault());
if (ret == null && isThrow) throw new ArgumentException($"{that.FullName} 类型无方法访问构造函数");
return ret;
}
readonly static ConcurrentDictionary _copyDataFunc = new ConcurrentDictionary();
readonly static Action _copyDataFuncEmpty = new Action((item1, item2) => { });
public static void CopyData(Type sourceType, object source, object target)
{
if (source == null) return;
if (target == null) return;
_copyDataFunc.GetOrAdd(sourceType, type =>
{
var sourceParamExp = Expression.Parameter(typeof(object), "sourceObject");
var targetParamExp = Expression.Parameter(typeof(object), "targetObject");
var sourceExp = Expression.Variable(type, "source");
var targetExp = Expression.Variable(type, "target");
var copyExps = new List();
var sourceFields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var field in sourceFields)
copyExps.Add(Expression.astign(Expression.MakeMemberAccess(targetExp, field), Expression.MakeMemberAccess(sourceExp, field)));
if (copyExps.Any() == false) return _copyDataFuncEmpty;
var bodyExp = Expression.Block(
new[] {
sourceExp, targetExp
},
new[] {
Expression.IfThen(
Expression.NotEqual(sourceParamExp, Expression.Constant(null)),
Expression.IfThen(
Expression.NotEqual(targetParamExp, Expression.Constant(null)),
Expression.Block(
Expression.astign(sourceExp, Expression.TypeAs(sourceParamExp, sourceType)),
Expression.astign(targetExp, Expression.TypeAs(targetParamExp, sourceType)),
Expression.IfThen(
Expression.NotEqual(sourceExp, Expression.Constant(null)),
Expression.IfThen(
Expression.NotEqual(sourceExp, Expression.Constant(null)),
Expression.Block(
copyExps.ToArray()
)
)
)
)
)
)
}
);
return Expression.Lambda(bodyExp, sourceParamExp, targetParamExp).Compile();
})(source, target);
}
static ConcurrentDictionary _dicSetEnsatyValueWithPropertyName = new ConcurrentDictionary();
public static void SetPropertyValue(Type sourceType, object source, string propertyOrField, object value)
{
if (source == null) return;
if (sourceType == null) sourceType = source.GetType();
_dicSetEnsatyValueWithPropertyName
.GetOrAdd(sourceType, et => new ConcurrentDictionary())
.GetOrAdd(propertyOrField, pf =>
{
var t = sourceType;
var parm1 = Expression.Parameter(typeof(object));
var parm2 = Expression.Parameter(typeof(string));
var parm3 = Expression.Parameter(typeof(object));
var var1Parm = Expression.Variable(t);
var exps = new List(new Expression[] {
Expression.astign(var1Parm, Expression.TypeAs(parm1, t))
});
var memberInfos = t.GetMember(pf, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(a => a.MemberType == MemberTypes.Field || a.MemberType == MemberTypes.Property);
foreach (var memberInfo in memberInfos) {
exps.Add(
Expression.astign(
Expression.MakeMemberAccess(var1Parm, memberInfo),
Expression.Convert(
parm3,
memberInfo.MemberType == MemberTypes.Field ? (memberInfo as FieldInfo)?.FieldType : (memberInfo as PropertyInfo)?.PropertyType
)
)
);
}
return Expression.Lambda(Expression.Block(new[] { var1Parm }, exps), new[] { parm1, parm2, parm3 }).Compile();
})(source, propertyOrField, value);
}
}
}