更新:我们熟悉Automapper,但是我们具有将数据表映射到模型的代码,并且可以轻松添加SqlTable属性。因此,我们希望能够通过简单的扩展做到这一点,而不必通过Automapper做到这一点。
我们的旧数据库有一些不良的命名习惯。在创建DateModels时,我们摆脱了很多前缀和扩展名。
我们正在努力的一种方法是获取从数据库中获取的EF实体,并使用反射将属性值复制到我们的DataModels中。
对于一个简单的类,我们可以完成所有工作。我们尚未解决的问题是如何处理收藏夹。
将是我们的sql表示例。
客户表
Cust_Id
Cust_Name
Cust_ProductId - FK to Product.Id
Product Table
Product_Id
Product_Name
那么我们的数据模型将是
public class CustomerModel : BaseCustomerModel
{
[SLSqlTable("Cust_Id")]
public int Id { get; set; }
[SLSqlTable("Cust_Name")]
public string Name { get; set; }
[SLSqlTable("Cust_ProductId")]
public string ProductId { get; set; }
[SLSqlTable("Products")]
public IList<BaseProduct> Products { get; set; }
}
public class BaseProductModel
{
[SLSqlTable("Product_Id")]
public int? Id { get; set; }
[SLSqlTable("Product_Name")]
public string Name { get; set; }
}
我们正在使用创建的SLSqlTableAttribute
来映射名称。
然后从互联网上,我们使用以下代码在属性之间复制数据。目前,除了收藏夹外,其他所有东西都可以正常使用,这就是我们要设法解决的问题。我们认为,我们先检测到一个集合,然后再发现一些如何递归地回调到CopyToModelProperties
中。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static T CopyToModelProperties<T>(this object source) where T : class, new()
{
T destination = new T();
Type typeDest = destination.GetType();
Type typeSrc = source.GetType();
// Collect all the valid properties to map
var results = from srcProp in typeSrc.GetProperties()
let targetProperty = typeDest.GetProperty(GetModelPropertyNameFromSqlAttribute(typeDest, srcProp.Name))
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
select new { sourceProperty = srcProp, targetProperty = targetProperty };
//map the properties
foreach (var props in results)
{
if (props.targetProperty.PropertyType.IsGenericType)
{
var _targetType = props.targetProperty.PropertyType.GetGenericArguments()[0];
// props.sourceProperty.PropertyType.MakeGenericType(_targetType).CopyToModelProperties((dynamic)List<>);
}
else
{
props.targetProperty.SetValue(destination, props.sourceProperty.GetValue(source, null), null);
}
}
return destination;
}
public static string GetModelPropertyNameFromSqlAttribute(Type model, string sqlName)
{
string _ret = "";
var _property = model.GetProperties().Where(w => w.GetCustomAttributes<SLSqlTableAttribute>().Where(w1 => w1.FieldName == sqlName).Any()).FirstOrDefault();
if(_property != null)
{
_ret = _property.Name;
}
return _ret;
}
以下是一些代码示例,可让客户使用其所有产品。
using (var _dc = new BulkTestEntities())
{
var _custs = _dc.Customers.Include("Products").ToList();
foreach(var cust in _custs)
{
_resp.Add(cust.CopyToModelProperties<CustomerModel>());
}
}
这很好用,并且如果条件检查IsGenericType有效,我们只需要弄清楚在返回客户时在此处处理产品集合的代码即可。
我们认为这将是对CopyToModelProperties的递归调用,因为“产品”内部可能也有一个集合,并且可能有一个集合,所以我们不想对代码进行硬编码。
所以问题是如何从上述if条件中获取props.sourceProperty并将SQL实体集合复制到DataModels集合中?
答案 0 :(得分:0)
我已经很长时间没有使用反射了,但是有点像这样的震撼没有出现或者没有反应。
在Internet上进行搜索时,发现了一些我拼凑的代码片段,以解决这个问题。它需要一些调整,但是可以处理我们要完成的任务。同样,不会处理所有可能的情况,但足以让我们开始并继续发展。同样,并非所有带有AutoMapper的PITA配置。我们编写了一个程序,该程序可以获取并获取sql表,生成类和属性,并且可以轻松地将属性添加到每个属性中,现在我们要做的就是调用下面的函数来映射数据。
我们需要的代码将使用我们想出的东西。
using (var _dc = new BulkTestEntities())
{
var _custs = _dc.Customers.Include("Products").Include("CustomerType").ToList();
// CopyPropertyData.CopyObjectPropertyData(_custs, _resp);
//foreach(var cust in _custs)
//{
// _resp.Add(cust.CopyToModelProperties<CustomerModel>());
//}
foreach (var cust in _custs)
{
CustomerModel _newCust = new CustomerModel();
SQLExtensions.CopyToModelProperties(cust, _newCust);
// CopyData.CopyObjectData(cust, _newCust);
_resp.Add(_newCust);
}
}
这是将处理IEnumerable和类的静态类。
public static void CopyToModelProperties(object source, object target)
{
Type _typeTarget = target.GetType();
Type _typeSrc = source.GetType();
if (_typeSrc.GetInterfaces().Any(a => a.Name == "IEnumerable") && _typeSrc.IsGenericType && _typeTarget.IsGenericType)
{
// Dynamic allows us to loop through a collection if it's type IEnumerable
dynamic _sourceList = source;
foreach (var _source in _sourceList)
{
// Create a temp class of the generic type of the target list
object _tempTarget = Activator.CreateInstance(_typeTarget.GetGenericArguments()[0]);
//Recursively call to map all child properties
CopyToModelProperties(_source, _tempTarget);
// Add to target collection passed in
target.GetType().GetMethod("Add").Invoke(target, new[] { _tempTarget });
}
}
else
{
var results = from srcProp in _typeSrc.GetProperties()
let targetProperty = _typeTarget.GetProperty(GetModelPropertyNameFromSqlAttribute(_typeTarget, srcProp.Name))
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
// && targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
select new { sourceProperty = srcProp, targetProperty = targetProperty };
foreach (var prop in results)
{
if (prop.targetProperty.CanWrite && prop.sourceProperty.CanRead)
{
object targetValue = prop.targetProperty.GetValue(target, null);
object sourceValue = prop.sourceProperty.GetValue(source, null);
if (sourceValue == null) { continue; }
//if (prop.sourceProperty.PropertyType.IsArray
// && prop.targetProperty.PropertyType.IsArray
// && sourceValue != null)
if(prop.sourceProperty.PropertyType.IsClass && prop.targetProperty.PropertyType != typeof(string))
{
var destinationClass = Activator.CreateInstance(prop.targetProperty.PropertyType);
object copyValue = prop.sourceProperty.GetValue(source);
CopyToModelProperties(copyValue, destinationClass);
prop.targetProperty.SetValue(target, destinationClass);
}// See if there is a better way to do this.
else if (prop.targetProperty.PropertyType.GetInterfaces().Any(a => a.Name == "IEnumerable") && prop.sourceProperty.PropertyType.IsGenericType
&& prop.targetProperty.PropertyType.IsGenericType
&& sourceValue != null)
{
CopyToModelList(source, target, prop.targetProperty, prop.sourceProperty, sourceValue);
}
else
{
// CopySingleData(source, target, prop.targetProperty, prop.sourceProperty, targetValue, sourceValue);
prop.targetProperty.SetValue(target, prop.sourceProperty.GetValue(source, null), null);
}
}
}
}
// return target;
}
private static void CopyToModelList(object source, object target, PropertyInfo piTarget, PropertyInfo piSource, object sourceValue)
{
// int _sourceLength = (int)source.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, source, null);
// First create a generic collection that matches the piTarget being passed in.
var _listType = typeof(List<>);
props.sourceProperty.PropertyType.GetGenericArguments();
var _genericTargetArgs = piTarget.PropertyType.GetGenericArguments();
var _concreteTargetType = _listType.MakeGenericType(_genericTargetArgs);
var _newtargetList = Activator.CreateInstance(_concreteTargetType);
dynamic _sourceList = piSource.GetValue(source, null);
foreach (var _source in _sourceList)
{
object _tempTarget = Activator.CreateInstance(piTarget.PropertyType.GetGenericArguments()[0]);
CopyToModelProperties(_source, _tempTarget);
// here we have to make recursive call to copy data and populate the target list.
//_targetList.Add(CopyObjectPropertyData(_source, (dynamic)new object()));
_newtargetList.GetType().GetMethod("Add").Invoke(_newtargetList, new[] { _tempTarget });
}
piTarget.SetValue(target, _newtargetList, null);
}
public static string GetModelPropertyNameFromSqlAttribute(Type model, string sqlName)
{
string _ret = "";
var _property = model.GetProperties().Where(w => w.GetCustomAttributes<SLSqlTableAttribute>().Where(w1 => w1.FieldName == sqlName).Any()).FirstOrDefault();
if(_property != null)
{
_ret = _property.Name;
}
return _ret;
}
答案 1 :(得分:0)
代码是如此可在此站点上编辑的PITA,从而创建了包括缓存在内的新答案。原始发布的代码具有60条记录的惊人表现。大约5秒钟。带有缓存的新代码,记录600条.36秒。在此代码中,我们具有审核字段,只有我们的映射器可以设置或更改“创建审核字段”。它可能不适用于您,如果是,请删除。下面需要的所有代码都是一个简单的Sql属性,如下所示。如果您的sql表字段名称与模型属性名称不同,请在模型属性上进行更新。比起Automapper容易得多,因为您不需要其他类型的配置,并且如果您的sql属性与模型属性匹配,则可以使用以下方法创建自己的mapper方法。
[AttributeUsage(AttributeTargets.Property)]
public class SLSqlTableAttribute : Attribute
{
protected string _fieldName { get; set; }
public string FieldName
{
get
{
return _fieldName;
}
set
{
_fieldName = value;
}
}
public SLSqlTableAttribute(string fieldName)
{
FieldName = fieldName;
}
}
public class SourceTargetProperties
{
public PropertyInfo SourceProperty { get; set; }
public PropertyInfo TargetProperty { get; set; }
}
public static class DataMapper
{
static Dictionary<string, List<SourceTargetProperties>> _dictTypeProperties = new Dictionary<string, List<SourceTargetProperties>>();
public static void CopyToSqlProperties(object source, object target, int? userId, DateTimeOffset? modifiedDateTime, bool isInsert)
{
Type _typeTarget = target.GetType();
Type _typeSrc = source.GetType();
// If we're passed in a collection
if (_typeSrc.GetInterfaces().Any(a => a.Name == "IEnumerable") && _typeSrc.IsGenericType && _typeTarget.IsGenericType)
{
dynamic _sourceList = source;
foreach (var _source in _sourceList)
{
object _tempTarget = Activator.CreateInstance(_typeTarget.GetGenericArguments()[0]);
CopyToModelProperties(_source, _tempTarget);
// here we have to make recursive call to copy data and populate the target list.
//_targetList.Add(CopyObjectPropertyData(_source, (dynamic)new object()));
// _newtargetList.GetType().GetMethod("Add").Invoke(_newtargetList, new[] { _tempTarget });
target.GetType().GetMethod("Add").Invoke(target, new[] { _tempTarget });
}
}
else
{
// Collect all the valid properties to map
if (!_dictTypeProperties.ContainsKey(_typeSrc.BaseType.Name))
{
_dictTypeProperties.Add(_typeSrc.BaseType.Name, (from srcProp in _typeSrc.GetProperties()
let targetProperty = _typeTarget.GetProperty(GetModelPropertyNameFromSqlAttribute(_typeTarget, srcProp.Name))
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
// && targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
select new SourceTargetProperties { SourceProperty = srcProp, TargetProperty = targetProperty }).ToList());
}
foreach (var prop in _dictTypeProperties[_typeSrc.BaseType.Name])
{
if (prop.TargetProperty.CanWrite && prop.SourceProperty.CanRead)
{
object targetValue = prop.TargetProperty.GetValue(target, null);
object sourceValue = prop.SourceProperty.GetValue(source, null);
if (sourceValue == null) { continue; }
//if (prop.sourceProperty.PropertyType.IsArray
// && prop.targetProperty.PropertyType.IsArray
// && sourceValue != null)
if (prop.SourceProperty.PropertyType.IsClass && prop.TargetProperty.PropertyType != typeof(string))
{
var destinationClass = Activator.CreateInstance(prop.TargetProperty.PropertyType);
object copyValue = prop.SourceProperty.GetValue(source);
CopyToModelProperties(copyValue, destinationClass);
prop.TargetProperty.SetValue(target, destinationClass);
}// See if there is a better way to do this.
else if (prop.TargetProperty.PropertyType.GetInterfaces().Any(a => a.Name == "IEnumerable") && prop.SourceProperty.PropertyType.IsGenericType
&& prop.TargetProperty.PropertyType.IsGenericType
&& sourceValue != null)
{
CopyToModelList(source, target, prop.TargetProperty, prop.SourceProperty, sourceValue);
}
else
{
string _targetPropertyName = prop.TargetProperty.Name;
if (modifiedDateTime.HasValue && (_targetPropertyName == "CreatedDateTime" || _targetPropertyName == "LastModifiedDateTime" || _targetPropertyName == "CreatedBy" ||
_targetPropertyName == "LastModifiedBy"))
{
switch (_targetPropertyName)
{
case "CreatedDateTime":
if (isInsert)
{
prop.TargetProperty.SetValue(target, modifiedDateTime, null);
}
break;
case "CreatedBy":
if (isInsert)
{
prop.TargetProperty.SetValue(target, userId, null);
}
break;
case "LastModifiedDateTime":
prop.TargetProperty.SetValue(target, modifiedDateTime, null);
break;
case "LastModifiedBy":
prop.TargetProperty.SetValue(target, userId, null);
break;
}
}
else
{
prop.TargetProperty.SetValue(target, prop.SourceProperty.GetValue(source, null), null);
}
}
}
}
}
}
/// <summary>
/// Copy from SQL EF Entities to our Models.
/// Models will have the sql table name as a SLSqlAttribute for this to work.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static void CopyToModelProperties(object source, object target)
{
Type _typeTarget = target.GetType();
Type _typeSrc = source.GetType();
if (_typeSrc.GetInterfaces().Any(a => a.Name == "IEnumerable") && _typeSrc.IsGenericType && _typeTarget.IsGenericType)
{
// figure out a way to take in collections here instead of having to loop through outside of code.
// int _sourceLength = (int)source.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, source, null);
var _listType = typeof(List<>);
//var _genericArgs = props.sourceProperty.PropertyType.GetGenericArguments();
//var _genericTargetArgs = _typeTarget.GetGenericArguments();
//var _concreteTargetType = _listType.MakeGenericType(_genericTargetArgs);
//var _newtargetList = Activator.CreateInstance(_concreteTargetType);
//_newtargetList = piSource.GetValue(source, null);
//var _genericTargetArgs
dynamic _sourceList = source;
//dynamic _sourceList = _typeSrc.GetValue(source, null);
// dynamic _targetList = piTarget.GetValue(target, null);
foreach (var _source in _sourceList)
{
object _tempTarget = Activator.CreateInstance(_typeTarget.GetGenericArguments()[0]);
CopyToModelProperties(_source, _tempTarget);
// here we have to make recursive call to copy data and populate the target list.
//_targetList.Add(CopyObjectPropertyData(_source, (dynamic)new object()));
// _newtargetList.GetType().GetMethod("Add").Invoke(_newtargetList, new[] { _tempTarget });
target.GetType().GetMethod("Add").Invoke(target, new[] { _tempTarget });
}
// _typeTarget.SetValue(target, _newtargetList, null);
}
else
{
// Collect all the valid properties to map
if (!_dictTypeProperties.ContainsKey(_typeSrc.BaseType.Name))
{
_dictTypeProperties.Add(_typeSrc.BaseType.Name, (from srcProp in _typeSrc.GetProperties()
let targetProperty = _typeTarget.GetProperty(GetModelPropertyNameFromSqlAttribute(_typeTarget, srcProp.Name))
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
// && targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
select new SourceTargetProperties { SourceProperty = srcProp, TargetProperty = targetProperty }).ToList());
}
foreach (var prop in _dictTypeProperties[_typeSrc.BaseType.Name])
{
if (prop.TargetProperty.CanWrite && prop.SourceProperty.CanRead)
{
object targetValue = prop.TargetProperty.GetValue(target, null);
object sourceValue = prop.SourceProperty.GetValue(source, null);
if (sourceValue == null) { continue; }
//if (prop.sourceProperty.PropertyType.IsArray
// && prop.targetProperty.PropertyType.IsArray
// && sourceValue != null)
if (prop.SourceProperty.PropertyType.IsClass && prop.TargetProperty.PropertyType != typeof(string))
{
var destinationClass = Activator.CreateInstance(prop.TargetProperty.PropertyType);
object copyValue = prop.SourceProperty.GetValue(source);
CopyToModelProperties(copyValue, destinationClass);
prop.TargetProperty.SetValue(target, destinationClass);
}// See if there is a better way to do this.
else if (prop.TargetProperty.PropertyType.GetInterfaces().Any(a => a.Name == "IEnumerable") && prop.SourceProperty.PropertyType.IsGenericType
&& prop.TargetProperty.PropertyType.IsGenericType
&& sourceValue != null)
{
CopyToModelList(source, target, prop.TargetProperty, prop.SourceProperty, sourceValue);
}
else
{
string _targetPropertyName = prop.TargetProperty.Name;
prop.TargetProperty.SetValue(target, prop.SourceProperty.GetValue(source, null), null);
}
}
}
}
// return target;
}
public static string GetModelPropertyNameFromSqlAttribute(Type model, string sqlName)
{
string _ret = "";
var _property = model.GetProperties().Where(w => w.GetCustomAttributes<SLSqlTableAttribute>().Where(w1 => w1.FieldName == sqlName).Any()).FirstOrDefault();
if (_property != null)
{
_ret = _property.Name;
}
return _ret;
}
private static void CopyToModelList(object source, object target, PropertyInfo piTarget, PropertyInfo piSource, object sourceValue)
{
// int _sourceLength = (int)source.GetType().InvokeMember("Count", BindingFlags.GetProperty, null, source, null);
// First create a generic collection that matches the piTarget being passed in.
var _listType = typeof(List<>);
//var _genericArgs = props.sourceProperty.PropertyType.GetGenericArguments();
var _genericTargetArgs = piTarget.PropertyType.GetGenericArguments();
var _concreteTargetType = _listType.MakeGenericType(_genericTargetArgs);
var _newtargetList = Activator.CreateInstance(_concreteTargetType);
//_newtargetList = piSource.GetValue(source, null);
//var _genericTargetArgs
dynamic _sourceList = piSource.GetValue(source, null);
// dynamic _targetList = piTarget.GetValue(target, null);
foreach (var _source in _sourceList)
{
object _tempTarget = Activator.CreateInstance(piTarget.PropertyType.GetGenericArguments()[0]);
CopyToModelProperties(_source, _tempTarget);
// here we have to make recursive call to copy data and populate the target list.
//_targetList.Add(CopyObjectPropertyData(_source, (dynamic)new object()));
_newtargetList.GetType().GetMethod("Add").Invoke(_newtargetList, new[] { _tempTarget });
}
piTarget.SetValue(target, _newtargetList, null);
}
}