我有以下方法:
protected override bool ModifyExistingEntity(Product entity, ProductModel item)
{
bool isModified = false;
if (entity.Title != item.Title)
{
isModified = true;
entity.Title = item.Title;
}
if (entity.ServerId != item.Id)
{
isModified = true;
entity.ServerId = item.Id;
}
return isModified;
}
Run Code Online (Sandbox Code Playgroud)
我想知道你是否可以建议一种更好的方法来实现该方法.
问题很明显:每个属性有5行几乎复制粘贴的代码太多了.可能是我的愿景中使用Func
-s/Expression
-s 的解决方案.
bst*_*zel 47
你有一个时间耦合的情况,即你正在混合检查实体是否随着分配而改变.如果将两者分开,则代码变得更清晰:
protected override bool ModifyExistingEntity(Product entity, ProductModel item)
{
bool isModified = this.IsEntityModified(entity, item);
if (isModified)
{
this.UpdateEntity(entity, item);
}
return isModified;
}
private bool IsEntityModified(Product entity, ProductModel item)
{
return entity.Title != item.Title || entity.ServerId != item.ServerId;
}
private void UpdateEntity(Product entity, ProductModel item)
{
entity.Title = item.Title;
entity.ServerId = item.Id;
}
Run Code Online (Sandbox Code Playgroud)
做任何聪明和时髦的东西(TM)Func<>
或类似的东西,在这种情况下似乎没有帮助,因为它不会清楚地传达你的意图.
Sri*_*vel 12
这样的事情应该有效
protected bool ModifyExistingEntity(Person entity, ProductModel item)
{
bool isModified = CompareAndModify(() => entity.Title = item.Title, () => entity.Title != item.Title);
isModified |= CompareAndModify(() => entity.ServerId = item.Id, () => entity.ServerId != item.Id);
return isModified;
}
private bool CompareAndModify(Action setter, Func<bool> comparator)
{
if (comparator())
{
setter();
return true;
}
return false;
}
Run Code Online (Sandbox Code Playgroud)
不确定这是否可读.这是主观的.
Pat*_*man 12
我认为这个答案的扩展可能适合你:
public static bool SetIfModified<CLeft, T>(Expression<Func<CLeft, T>> exprLeft, CLeft leftType, T rightValue)
{
var getterLeft = exprLeft.Compile();
if (EqualityComparer<T>.Default.Equals(getterLeft(leftType), rightValue))
{
var newValueLeft = Expression.Parameter(exprLeft.Body.Type);
var assignLeft = Expression.Lambda<Action<CLeft, T>>(Expression.Assign(exprLeft.Body, newValueLeft), exprLeft.Parameters[0], newValueLeft);
var setterLeft = assignLeft.Compile();
setterLeft(leftType, rightValue);
return true;
}
else
{
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
它需要一个表达式来检查值.它动态编译和执行它.
像这样使用它:
public class Product { public string Title { get; set; } }
public class ProductModel { public string Title { get; set; } }
static void Main(string[] args)
{
Product lc = new Product();
ProductModel rc = new ProductModel();
rc.Title = "abc";
bool modified = SetIfModified(l => l.Title, lc, r.Title);
// modified is true
// lc.Title is "abc"
}
Run Code Online (Sandbox Code Playgroud)
Kob*_*obi 10
另一种方法 - 通常当我们有重复的代码时,实际上很简单,可能非常快.在这种情况下,每个重复的if
块都不相同 - 它拥有一点知识 - 从一个属性到另一个属性的映射.
编写和维护重复的块很烦人.
避免编写有用的重复代码的一种方法是自动生成它.
使用我的解决方案,映射非常简单:
var mappings = new []{
new Mapper("ProductModel", "Product")
{
"Title", // ProductModel.Title goes to Product.Title
{"Id", "ServiceId"}, // ProductModel.Id goes to Product.ServiceId
},
};
Run Code Online (Sandbox Code Playgroud)
这是一个t4文本模板(Visual Studio的内置功能):
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#
// Consider including the namespace in the class names.
// You only need to change the mappings.
var product = new Mapper("Product", "ProductEntity") { "Name", {"Id", "ServiceId"} };
var person = new Mapper("Person", "DbPerson") { "Employee", {"Name", "FullName"}, {"Addredd", "HomeAddress"} };
var mappings = new [] {product, person};
#>
// !!!
// !!! Do not modify this file, it is automatically generated. Change the .tt file instead. !!!
// !!!
namespace Your.Mapper
{
partial class Mapper
{
<# foreach(var mapping in mappings) {
#>/// <summary>
/// Set <paramref name="target"/> properties by copying them from <paramref name="source"/>.
/// </summary>
/// <remarks>Mapping:<br/>
<#foreach(var property in mapping){
#>/// <see cref="<#=mapping.SourceType#>.<#=property.SourceProperty#>"/> ? <see cref="<#=mapping.TargetType#>.<#=property.TargetProperty#>"/> <br/>
<#}
#>/// </remarks>
/// <returns><c>true</c> if any property was changed, <c>false</c> if all properties were the same.</returns>
public bool ModifyExistingEntity(<#=mapping.SourceType#> source, <#=mapping.TargetType#> target)
{
bool dirty = false;
<# foreach(var property in mapping) {
#>if (target.<#=property.TargetProperty#> != source.<#=property.SourceProperty#>)
{
dirty = true;
target.<#=property.TargetProperty#> = source.<#=property.SourceProperty#>;
}
<#}
#>return dirty;
}
<#
}
#>
}
}
<#+
class Mapper : IEnumerable<PropertyMapper>
{
private readonly List<PropertyMapper> _properties;
public Mapper(string sourceType, string targetType)
{
SourceType = sourceType;
TargetType = targetType;
_properties = new List<PropertyMapper>();
}
public string SourceType { get; set; }
public string TargetType { get; set; }
public void Add(string fieldName)
{
_properties.Add(new PropertyMapper {SourceProperty = fieldName, TargetProperty = fieldName});
}
public void Add(string sourceProperty, string targetProperty)
{
_properties.Add(new PropertyMapper { SourceProperty = sourceProperty, TargetProperty = targetProperty });
}
IEnumerator<PropertyMapper> IEnumerable<PropertyMapper>.GetEnumerator() { return _properties.GetEnumerator(); }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _properties.GetEnumerator(); }
}
class PropertyMapper
{
public string SourceProperty { get; set; }
public string TargetProperty { get; set; }
}
#>
Run Code Online (Sandbox Code Playgroud)
此模板生成以下代码:https://gist.github.com/kobi/d52dd1ff27541acaae10
source
和target
,并改变它们的顺序所以source
永远是第一位.有人担心我使用的是字符串而不是真正的属性.虽然在这种情况下这是一个小问题(编译输出),但这里有一个适用于您的真实对象的附加功能.
在顶部,添加此(第三个应该是您的命名空间):
<#@ assembly name="$(TargetPath)" #>
<#@ import namespace="System.Linq.Expressions" #>
<#@ import namespace="ConsoleApplicationT4So29913514" #>
Run Code Online (Sandbox Code Playgroud)
在底部,添加:
class Mapper<TSource, TTarget> : Mapper
{
public Mapper()
: base(typeof(TSource).FullName, typeof(TTarget).FullName)
{
}
private static string GetExpressionMemberAccess(LambdaExpression getProperty)
{
var member = (MemberExpression)getProperty.Body;
//var lambdaParameterName = (ParameterExpression)member.Expression;
var lambdaParameterName = getProperty.Parameters[0]; // `x` in `x => x.PropertyName`
var labmdaBody = member.ToString();
//will not work with indexer.
return labmdaBody.Substring(lambdaParameterName.Name.Length + 1); //+1 to remove the `.`, get "PropertyName"
}
public void Add<TProperty>(Expression<Func<TSource, TProperty>> getSourceProperty, Expression<Func<TTarget, TProperty>> getTargetProperty)
{
Add(GetExpressionMemberAccess(getSourceProperty), GetExpressionMemberAccess(getTargetProperty));
}
/// <summary>
/// The doesn't really make sense, but we assume we have <c>source=>source.Property</c>, <c>target=>target.Property</c>
/// </summary>
public void Add<TProperty>(Expression<Func<TSource, TProperty>> getProperty)
{
Add(GetExpressionMemberAccess(getProperty));
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
var mappings = new Mapper[] {
new Mapper<Student,StudentRecord>
{
{s=>s.Title, t=>t.EntityTitle},
{s=>s.StudentId, t=>t.Id},
s=>s.Name,
s=>s.LuckyNumber,
},
new Mapper<Car,RaceCar>
{
c=>c.Color,
c=>c.Driver,
{c=>c.Driver.Length, r=>r.DriverNameDisplayWidth},
},
};
Run Code Online (Sandbox Code Playgroud)
整个文件应如下所示:https://gist.github.com/kobi/6423eaa13cca238447a8
输出看起来仍然相同:https://gist.github.com/kobi/3508e9f5522a13e1b66b
笔记:
nameof()
运算符,这是表达式和无魔术字符串之间的一个很好的折衷.没有魔术棒来简化这一点.
您可以让实体本身提供IsModified属性,然后由属性设置者设置,例如:
public string Title {
get { return _title; }
set {
if (value != _title)
{
_title = value;
IsModified = true;
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果这是太多的工作,你的解决方案是好的.
如果您想让它可读,您可以为此目的创建一个类,使用非常简单,避免重复代码:
protected override bool ModifyExistingEntity(Product entity, ProductModel item)
{
return new Modifier<Product>(entity)
.SetIfNeeded(e => e.Title, item.Title);
.SetIfNeeded(e => e.ServerId, item.Id);
.EntityWasModified;
}
Run Code Online (Sandbox Code Playgroud)
执行:
我从Patrick Hofman那里获取了一些代码,用getter表达式生成一个setter.
public class Modifier<TEntity>
{
public Modifier(TEntity entity)
{
Entity = entity;
}
public TEntity Entity { get; private set; }
public bool EntityWasModified { get; private set; }
public Modifier<TEntity> SetIfNeeded<TProperty>(Expression<Func<TEntity, TProperty>> entityPropertyGetter, TProperty modelValue)
{
var getter = entityPropertyGetter.Compile();
var setter = GetSetterExpression(entityPropertyGetter).Compile();
if (!object.Equals(getter(Entity), modelValue))
{
setter(Entity, modelValue);
EntityWasModified = true;
}
return this;
}
private static Expression<Action<TEntity, TProperty>> GetSetterExpression(Expression<Func<TEntity, TProperty>> getterExpression)
{
var newValue = Expression.Parameter(getterExpression.Body.Type);
return Expression.Lambda<Action<TEntity, TProperty>>(
Expression.Assign(getterExpression.Body, newValue),
getterExpression.Parameters[0], newValue);
}
}
Run Code Online (Sandbox Code Playgroud)
您可能希望缓存结果.Compile
以提高性能.
归档时间: |
|
查看次数: |
3446 次 |
最近记录: |