Skip to content

Instantly share code, notes, and snippets.

@kobi
Last active August 29, 2015 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kobi/6423eaa13cca238447a8 to your computer and use it in GitHub Desktop.
Save kobi/6423eaa13cca238447a8 to your computer and use it in GitHub Desktop.
Example output for Stack Overflow question: How to simplify repeating if-then-assign construction? - http://stackoverflow.com/a/29918452/7586
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="$(TargetPath)" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq.Expressions" #>
<#@ import namespace="ConsoleApplicationT4So29913514" #>
<#@ output extension=".generated.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 product = new Mapper<Student,StudentRecord>{{}};
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},
},
};
#>
// !!!
// !!! 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; }
}
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));
}
}
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment