Related
I am versed in the basics of Expression Trees, but I am having difficulty with this implementation:
I need to modify an instance of a class/interface, by finding every string property and encrypting it. I also need to recursively do the same to every "child" class/interface property, all the way down the rabbit hole. This includes IEnumerable<T> where T : class as well. I have the encryption sorted, but creating the expression try to do this for any T passed in is frankly beyond my understanding at this point.
I tried implementing this with reflection but performance quickly became an issue - here's what I've got so far:
Using the AggregateHelper and PropertyHelper classes from this post I was able to perform the basic string encryption:
Simple Test Classes:
public class SimpleEncryptionTest
{
public string String1 { get; set; }
}
public class NestedClassEncryptionTest
{
public string SomeString { get; set; }
public SimpleEncryptionTest Inner { get; set; }
}
public class ListClassEncryptionTest
{
public List<string> StringList { get; set; }
}
The unit tests I'm using to verify results:
[TestMethod]
public void Encryption_works_on_a_simple_class()
{
var testString = "this is only a test. If this had been a real emergency...";
var sut = new SimpleEncryptionTest() { String1 = testString };
sut.EncryptStringProperties();
Assert.AreNotEqual(testString, sut.String1);
}
[TestMethod]
public void Round_trip_works_on_a_simple_class()
{
var testString = "what string should I use?";
var sut = new SimpleEncryptionTest() { String1 = testString };
sut.EncryptStringProperties();
sut.DecryptStringProperties();
Assert.AreEqual(testString, sut.String1);
}
[TestMethod]
public void Round_trip_works_in_nested_class_scenario()
{
var outerString = "what is your name?";
var innerString = "Tony; what's your name?";
var sut = new NestedClassEncryptionTest{
SomeString = outerString,
Inner = new SimpleEncryptionTest(){String1 = innerString }
};
sut.EncryptStringProperties();
Assert.AreNotEqual(outerString, sut.SomeString);
Assert.AreNotEqual(innerString, sut.Inner.String1);
sut.DecryptStringProperties();
Assert.AreEqual(outerString, sut.SomeString);
Assert.AreEqual(innerString, sut.Inner.String1);
}
[TestMethod]
public void Round_trip_works_on_lists()
{
var testone = "one";
var testtwo = "two";
var testStrings = new List<string>() { testone, testtwo };
var sut = new ListClassEncryptionTest() { StringList = testStrings };
sut.EncryptStringProperties();
Assert.AreNotEqual(testone, sut.StringList[0]);
Assert.AreNotEqual(testtwo, sut.StringList[1]);
sut.DecryptStringProperties();
Assert.AreEqual(testone, sut.StringList[0]);
Assert.AreEqual(testtwo, sut.StringList[1]);
}
And the extension methods I'm using to Encrypt/Decrypt the values:
/// <summary>
/// Iterates through an Object's properties and encrypts any strings it finds,
/// recursively iterating any class or interface Properties.
/// </summary>
/// <typeparam name="T">Any type</typeparam>
/// <param name="genericObj">The object to encrypt</param>
/// <returns>The original object, with its string values encrypted.</returns>
public static T EncryptStringProperties<T>(this T obj)
where T : class
{
return Crypt(obj, EncryptStringAction);
}
/// <summary>
/// Iterates through an Object's properties and decrypts any strings it finds,
/// recursively iterating any class or interface Properties.
/// </summary>
/// <typeparam name="T">Any type</typeparam>
/// <param name="genericObj">The object to decrypt</param>
/// <returns>The original object, with its string values decrypted.</returns>
public static T DecryptStringProperties<T>(this T obj)
{
return Crypt(obj, DecryptStringAction);
}
private static T Crypt<T>(T obj, Action<PropertyHelper<T, string>, T> action)
{
var stringProperties = new AggregateHelper<T, string>();
foreach (var property in stringProperties.Properties)
{
var propertyHelper = stringProperties[property];
action(propertyHelper, obj);
}
// how do I find the nested classes, interfaces and lists
// using Expression Trees to feed them through the same processing?
return obj;
}
private static void EncryptStringAction<T>(PropertyHelper<T, string> prop, T genericObj)
{
var plainTextValue = (string)prop.GetValue(genericObj);
prop.SetValue(genericObj, plainTextValue.ToEncryptedString());
}
private static void DecryptStringAction<T>(PropertyHelper<T, string> prop, T genericObj)
{
var encryptedValue = (string)prop.GetValue(genericObj);
prop.SetValue(genericObj, encryptedValue.ToDecryptedString());
}
This works great as far as it goes; it encrypts string properties on any object, but I need to take it further - I need some way to recursively go "down the rabbit hole" - to create an AggregateHelper that selects properties that are instance objects (classes or interfaces) and feed those through the .EncryptStringProperties() extension method, as well as handling any IEnumerable with string values.
Try the following extension. It generates replacement Expression Tree for each object and compiles delegates for fast string replacement. All delegates are cached and replacement should be fast. It also handles recursive references and should omit Stack Overflow.
StringPropReplacer.ReplaceStrings(obj, s => Encrypt(s));
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace StringReplacer
{
public static class StringPropReplacer
{
private static ParameterExpression _strParam = Expression.Parameter(typeof(string), "str");
private static ParameterExpression _originalValue = Expression.Variable(typeof(string), "original");
private static ParameterExpression _newValue = Expression.Variable(typeof(string), "newValue");
private static ParameterExpression _visitedParam = Expression.Parameter(typeof(HashSet<object>), "visited");
private static ParameterExpression _replacerParam = Expression.Parameter(typeof(Func<string, string>), "replacer");
private static ParameterExpression[] _blockVariables = new [] {_originalValue, _newValue};
private static void ReplaceObject<T>(T obj, HashSet<object> visited, Func<string, string> replacer)
{
ReflectionHolder<T>.ReplaceObject(obj, visited, replacer);
}
private static void ReplaceObjects<T>(IEnumerable<T> objects, HashSet<object> visited, Func<string, string> replacer)
{
ReflectionHolder<T>.ReplaceObjects(objects, visited, replacer);
}
public class ObjectReferenceEqualityComparer<T> : IEqualityComparer<T>
where T : notnull
{
public static IEqualityComparer<T> Default = new ObjectReferenceEqualityComparer<T>();
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
return ReferenceEquals(x, y);
}
public int GetHashCode(T obj)
{
if (obj == null)
return 0;
return RuntimeHelpers.GetHashCode(obj);
}
#endregion
}
private static class ReflectionHolder<T>
{
private static Action<T, HashSet<object>, Func<string, string>> _replacer;
static ReflectionHolder()
{
var objParam = Expression.Parameter(typeof(T), "obj");
var blockBody = new List<Expression>();
foreach (var prop in typeof(T).GetProperties())
{
if (prop.PropertyType == typeof(string) && prop.CanRead && prop.CanWrite)
{
var propExpression = Expression.MakeMemberAccess(objParam, prop);
blockBody.Add(Expression.Assign(_originalValue, propExpression));
blockBody.Add(Expression.Assign(_newValue, Expression.Invoke(_replacerParam, _originalValue)));
blockBody.Add(Expression.IfThen(Expression.NotEqual(_originalValue, _newValue),
Expression.Assign(propExpression, _newValue)));
}
else if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
{
var intf = prop.PropertyType
.GetInterfaces()
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
if (intf != null)
{
var propExpression = Expression.MakeMemberAccess(objParam, prop);
blockBody.Add(Expression.Call(typeof(StringPropReplacer), "ReplaceObjects",
intf.GetGenericArguments(), propExpression, _visitedParam, _replacerParam
));
}
}
else if (prop.PropertyType.IsClass)
{
var propExpression = Expression.MakeMemberAccess(objParam, prop);
blockBody.Add(Expression.Call(typeof(StringPropReplacer), "ReplaceObject",
new[] {prop.PropertyType}, propExpression, _visitedParam, _replacerParam
));
}
}
if (blockBody.Count == 0)
_replacer = (o, v, f) => { };
else
{
var replacerExpr = Expression.Lambda<Action<T, HashSet<object>, Func<string, string>>>(
Expression.Block(_blockVariables, blockBody), objParam, _visitedParam, _replacerParam);
_replacer = replacerExpr.Compile();
}
}
public static void ReplaceObject(T obj, HashSet<object> visited, Func<string, string> replacer)
{
if (obj == null)
return;
if (!visited.Add(obj))
return;
_replacer(obj, visited, replacer);
}
public static void ReplaceObjects(IEnumerable<T> objects, HashSet<object> visited, Func<string, string> replacer)
{
if (objects == null)
return;
if (!visited.Add(objects))
return;
foreach (var obj in objects)
{
ReplaceObject(obj, visited, replacer);
}
}
}
public static void ReplaceStrings<T>(T obj, Func<string, string> replacer)
{
ReplaceObject(obj, new HashSet<object>(ObjectReferenceEqualityComparer<object>.Default), replacer);
}
}
}
I'm trying to make a utility function that can Swap two property values given by two lambda expressions - assuming that both expressions indicate properties that have a getter and a setter:
Swap(() => John.Lunch, () => Jimmy.Lunch);
I imagine the method would need to look something like this, but I'm having trouble pulling it together.
private static void Swap<TProperty>(
Expression<Func<TProperty>> first,
Expression<Func<TProperty>> second)
{
PropertyInfo firstProp = (PropertyInfo)((MemberExpression)first.Body).Member;
PropertyInfo secondProp = (PropertyInfo)((MemberExpression)second.Body).Member;
object firstObj = (((first.Body as MemberExpression).Expression as MemberExpression)
.Expression as ConstantExpression).Value;
object secondObj = (((second.Body as MemberExpression).Expression as MemberExpression)
.Expression as ConstantExpression).Value;
TProperty temp = (TProperty)firstProp.GetValue(firstObj);
firstProp.SetValue(firstObj, secondProp.GetValue(secondObj));
secondProp.SetValue(secondObj, temp);
}
Getting to the "subject" object of the expression is proving to be difficult, although I'm pretty sure it's possible.
You can write the swap itself with Expression trees:
private static void Swap<TProperty>(
Expression<Func<TProperty>> first,
Expression<Func<TProperty>> second)
{
var firstMember = first.Body as MemberExpression;
var secondMember = second.Body as MemberExpression;
var variable = Expression.Variable(typeof(TProperty));
var firstMemberAccess = Expression.MakeMemberAccess(firstMember.Expression, firstMember.Member);
var secondMemberAccess = Expression.MakeMemberAccess(secondMember.Expression, secondMember.Member);
var block = Expression.Block(new []{ variable },
Expression.Assign(variable, firstMemberAccess),
Expression.Assign(firstMemberAccess, secondMemberAccess),
Expression.Assign(secondMemberAccess, variable)
);
Expression.Lambda<Action>(block).Compile()();
}
Example:
class A { public int P { get; set; } }
class B { public int P2 { get; set; } }
var a = new A { P = 5 };
var b = new B { P2 = 10 };
Swap(() => a.P, () => b.P2);
I tried to write code as self explanatory as possible so I will let the comments do the talking. The code needs error checking but I will leave it up to you. Here is the working minimum. I chose a static class for Swapper but you can choose to use as instance object then use DI container too.
public static class Swapper
{
/// <summary>
/// Key used for look-ups.
/// </summary>
private struct Key
{
private readonly Type _tt1;
private readonly Type _tt2;
private readonly MemberInfo _m11;
private readonly MemberInfo _m12;
public Key(Type t1, Type t2, MemberInfo m1, MemberInfo m2)
{
_tt1 = t1;
_tt2 = t2;
_m11 = m1;
_m12 = m2;
}
public override bool Equals(object obj)
{
switch (obj)
{
case Key key:
return _tt1 == key._tt1 && _tt2 == key._tt2 && _m11 == key._m11 && _m12 == key._m12;
default:
return false;
}
}
public override int GetHashCode()
{
unchecked
{
var hashCode = (_tt1 != null ? _tt1.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (_tt2 != null ? _tt2.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (_m11 != null ? _m11.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ (_m12 != null ? _m12.GetHashCode() : 0);
return hashCode;
}
}
}
/// <summary>
/// This is the cache used for compiled actions. Because compiling is time consuming, I think it is better to cache.
/// </summary>
private static readonly ConcurrentDictionary<Key, object> CompiledActionsCache = new ConcurrentDictionary<Key, object>();
/// <summary>
/// Main Function which does the swapping
/// </summary>
public static void Swap<TSource, TDestination, TPropertySource>(TSource source, TDestination destination, Expression<Func<TSource, TPropertySource>> first, Expression<Func<TDestination, TPropertySource>> second)
{
//Try to get value from the cache or if it is cache miss then use Factory method to create Compiled Action
var swapper = (Action<TSource, TDestination>)CompiledActionsCache.GetOrAdd(GetKey(first, second), k => Factory(first, second));
//Do the actual swapping.
swapper(source, destination);
}
/// <summary>
/// Our factory method which does all the heavy lfiting fo creating swapping actions.
/// </summary>
private static Action<TSource, TDestination> Factory<TSource, TDestination, TPropertySource>(Expression<Func<TSource, TPropertySource>> first, Expression<Func<TDestination, TPropertySource>> second)
{
//////////////This is our aim/////////////
//// var temp = obj1.Property; /////
//// obj1.Property = obj2.Property; /////
//// obj2.Property = temp; /////
//////////////////////////////////////////
// Temp value for required for swapping
var tempValue = Expression.Variable(typeof(TPropertySource), "temp_value");
// Expression assignment
// first.body is already accesing property, just use it as it is :)
var assignToTemp = Expression.Assign(tempValue, first.Body);
// Expression assignment
// second.body is already accesing property, just use it as it is :)
var secondToFirst = Expression.Assign(first.Body, second.Body);
// Final switch here
var tempToSecond = Expression.Assign(second.Body, tempValue);
// Define an expression block which has all the above assignments as expressions
var blockExpression = Expression.Block(new[] { tempValue }, assignToTemp, secondToFirst, tempToSecond);
// An expression itself is not going to swap values unless it is compiled into a delegate
// (obj1, obj2) => blockExpression from the previous line
return Expression.Lambda<Action<TSource, TDestination>>(blockExpression, first.Parameters[0], second.Parameters[0]).Compile();
}
/// <summary>
/// Key creator method.
/// </summary>
private static Key GetKey<TSource, TDestination, TPropertySource>(Expression<Func<TSource, TPropertySource>> first, Expression<Func<TDestination, TPropertySource>> second)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var sourcePropertyInfo = ((MemberExpression)first.Body).Member;
var destinationPorpertyInfo = ((MemberExpression)second.Body).Member;
return new Key(sourceType, destinationType, sourcePropertyInfo, destinationPorpertyInfo);
}
}
Let us see how to use Swapper in action
public class From
{
public byte FromProperty { get; set; }
}
public class To
{
public byte ToProperty { get; set; }
}
var from = new From();
from.FromProperty = 5;
var to = new To();
Swapper.Swap(from, to, f => f.FromProperty, t => t.ToProperty);
I have a working PATCH for my user class with Delta in Web API 2. By using the .patch method I can easily detect only the changes that were sent over and then update accordingly, rather than have to receive the entire user!
The problem is there are several fields that I want to protect so they are never updated.
I saw one example on SO but it didn't leverage Delta rather seemed to be slightly more dated and practically wrote all of the patch code by hand. Is there not a way to easily tell OData's patch to skip over properties you designate (maybe I need to override patch and tell it to avoid some properties)?
How would I even begin to go about doing this (or what should I search for / research to get started)? Do action filters / validation have a role here? Do I look into model binding? Is it overriding patch?
Thanks!
Depending on what you want to do if someone tries to update protected fields you can either:
Update only fields that can be modified. For this you can construct new Delta with only these fields like this:
Delta<User> filteredDelta = new Delta<User>();
if (originalDelta.GetChangedPropertyNames().Contains("FirstName"))
{
filteredDelta.TrySetPropertyValue("FirstName", originalDelta.GetEntity().FirstName);
}
if (originalDelta.GetChangedPropertyNames().Contains("LastName"))
{
filteredDelta.TrySetPropertyValue("LastName", originalDelta.GetEntity().LastName);
}
filteredDelta.Patch(selectedUser);
Fail the PATCH request (I would say this is preferred and least surprising way to deal with such requests). This would be even simpler:
if (originalDelta.GetChangedPropertyNames().Contains("ModifiedDate"))
{
return InternalServerError(new ArgumentException("Attribue is read-only", "ModifiedDate"));
}
There's a couple of possibilities, depending on you use case...
You want to exclude the changes if they are supplied
You want to throw an error if non-editable fields are updated.
Start with an attribute to mark appropriate properties
/// <summary>
/// Marks a property as non-editable.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class NonEditableAttribute : Attribute
{
}
Then we can write some extensions against Delta to take advantage of this
public static class PatchExtensions
{
/// <summary>
/// Get the properties of a type that are non-editable.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IList<string> NonEditableProperties(this Type type)
{
return type.GetProperties().Where(x => Attribute.IsDefined(x, typeof(NonEditableAttribute))).Select(prop => prop.Name).ToList();
}
/// <summary>
/// Get this list of non-editable changes in a <see cref="Delta{T}"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="delta"></param>
/// <returns></returns>
public static IList<string> NonEditableChanges<T>(this Delta<T> delta)
where T : class
{
var nec = new List<string>();
var excluded = typeof(T).NonEditableProperties();
nec.AddRange(delta.GetChangedPropertyNames().Where(x => excluded.Contains(x)));
return nec;
}
/// <summary>
/// Exclude changes from a <see cref="Delta{T}"/> based on a list of property names
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="delta"></param>
/// <param name="excluded"></param>
/// <returns></returns>
public static Delta<T> Exclude<T>(this Delta<T> delta, IList<string> excluded)
where T : class
{
var changed = new Delta<T>();
foreach (var prop in delta.GetChangedPropertyNames().Where(x => !excluded.Contains(x)))
{
object value;
if (delta.TryGetPropertyValue(prop, out value))
{
changed.TrySetPropertyValue(prop, value);
}
}
return changed;
}
/// <summary>
/// Exclude changes from a <see cref="Delta{T}"/> where the properties are marked with <see cref="NonEditableAttribute"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="delta"></param>
/// <returns></returns>
public static Delta<T> ExcludeNonEditable<T>(this Delta<T> delta)
where T : class
{
var excluded = typeof(T).NonEditableProperties();
return delta.Exclude(excluded);
}
}
And a domain class
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
[NonEditable]
public string SecurityId { get; set; }
}
Finally your controller can then take advantage of this in the Patch method
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Customer> delta)
{
var patch = delta.ExcludeNonEditable();
// TODO: Your patching action here
}
or
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<Customer> delta)
{
var nonEditable = delta.NonEditableChanges();
if (nonEditable.Count > 0)
{
throw new HttpException(409, "Cannot update as non-editable fields included");
}
// TODO: Your patching action here
}
I had the same need and I ended up writing an extension method to Delta that accepts additional parameters to limit which fields to update (similar to TryUpDateModel)
I know there must be a better way to do this, but for now this works.
I had to recreate some of the Delta private methods and classes. Most of the code is from https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Http.OData/OData/Delta.cs, https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/OData/src/System.Web.Http.OData/OData/PropertyAccessor.cs and https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Http.OData/OData/CompiledPropertyAccessor.cs (or similar, these are not the exact url's I copied from)
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Web;
using System.Reflection;
using System.Linq.Expressions;
namespace MyProject.ODataExtensions
{
public static class ODataExtensions
{
public static void Patch<TEntityType>(this System.Web.OData.Delta<TEntityType> d, TEntityType original, String[] includedProperties, String[] excludedProperties) where TEntityType : class
{
Dictionary<string, PropertyAccessor<TEntityType>> _propertiesThatExist = InitializePropertiesThatExist<TEntityType>();
var changedProperties = d.GetChangedPropertyNames();
if (includedProperties != null) changedProperties = changedProperties.Intersect(includedProperties);
if (excludedProperties != null) changedProperties = changedProperties.Except(excludedProperties);
PropertyAccessor<TEntityType>[] array = (
from s in changedProperties
select _propertiesThatExist[s]).ToArray();
var array2 = array;
for (int i = 0; i < array2.Length; i++)
{
PropertyAccessor<TEntityType> propertyAccessor = array2[i];
propertyAccessor.Copy(d.GetEntity(), original);
}
}
private static Dictionary<string, PropertyAccessor<T>> InitializePropertiesThatExist<T>() where T : class
{
Type backingType = typeof(T);
return backingType.GetProperties()
.Where(p => p.GetSetMethod() != null && p.GetGetMethod() != null)
.Select<PropertyInfo, PropertyAccessor<T>>(p => new CompiledPropertyAccessor<T>(p))
.ToDictionary(p => p.Property.Name);
}
internal abstract class PropertyAccessor<TEntityType> where TEntityType : class
{
protected PropertyAccessor(PropertyInfo property)
{
if (property == null)
{
throw new System.ArgumentException("Property cannot be null","property");
}
Property = property;
if (Property.GetGetMethod() == null || Property.GetSetMethod() == null)
{
throw new System.ArgumentException("Property must have public setter and getter", "property");
}
}
public PropertyInfo Property
{
get;
private set;
}
public void Copy(TEntityType from, TEntityType to)
{
if (from == null)
{
throw new System.ArgumentException("Argument cannot be null", "from");
}
if (to == null)
{
throw new System.ArgumentException("Argument cannot be null", "to");
}
SetValue(to, GetValue(from));
}
public abstract object GetValue(TEntityType entity);
public abstract void SetValue(TEntityType entity, object value);
}
internal class CompiledPropertyAccessor<TEntityType> : PropertyAccessor<TEntityType> where TEntityType : class
{
private Action<TEntityType, object> _setter;
private Func<TEntityType, object> _getter;
public CompiledPropertyAccessor(PropertyInfo property)
: base(property)
{
_setter = MakeSetter(Property);
_getter = MakeGetter(Property);
}
public override object GetValue(TEntityType entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
return _getter(entity);
}
public override void SetValue(TEntityType entity, object value)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_setter(entity, value);
}
private static Action<TEntityType, object> MakeSetter(PropertyInfo property)
{
Type type = typeof(TEntityType);
ParameterExpression entityParameter = Expression.Parameter(type);
ParameterExpression objectParameter = Expression.Parameter(typeof(Object));
MemberExpression toProperty = Expression.Property(Expression.TypeAs(entityParameter, property.DeclaringType), property);
UnaryExpression fromValue = Expression.Convert(objectParameter, property.PropertyType);
BinaryExpression assignment = Expression.Assign(toProperty, fromValue);
Expression<Action<TEntityType, object>> lambda = Expression.Lambda<Action<TEntityType, object>>(assignment, entityParameter, objectParameter);
return lambda.Compile();
}
private static Func<TEntityType, object> MakeGetter(PropertyInfo property)
{
Type type = typeof(TEntityType);
ParameterExpression entityParameter = Expression.Parameter(type);
MemberExpression fromProperty = Expression.Property(Expression.TypeAs(entityParameter, property.DeclaringType), property);
UnaryExpression convert = Expression.Convert(fromProperty, typeof(Object));
Expression<Func<TEntityType, object>> lambda = Expression.Lambda<Func<TEntityType, object>>(convert, entityParameter);
return lambda.Compile();
}
}
}
}
I have a performance problem because I use reflection and GetCustomAttributes for my data access. The performance profiler detected it. I have an extension method like this:
public static class DataRowExtensions
{
/// <summary>
/// Maps DataRow objecto to entity T depending on the defined attributes.
/// </summary>
/// <typeparam name="T">Entity to map.</typeparam>
/// <param name="rowInstance">DataRow instance.</param>
/// <returns>Instance to created entity.</returns>
public static T MapRow<T>(this DataRow rowInstance) where T : class, new()
{
//Create T item
T instance = new T();
IEnumerable<PropertyInfo> properties = typeof(T).GetProperties();
MappingAttribute map;
DataColumn column;
foreach (PropertyInfo item in properties)
{
//check if custom attribute exist in this property
object[] definedAttributes = item.GetCustomAttributes(typeof(MappingAttribute), false);
// Tiene atributos
if (definedAttributes != null && definedAttributes.Length == 1)
{
//recover first attribute
map = definedAttributes.First() as MappingAttribute;
column = rowInstance.Table.Columns.OfType<DataColumn>()
.Where(c => c.ColumnName == map.ColumnName)
.SingleOrDefault();
if (column != null)
{
object dbValue = rowInstance[column.ColumnName];
object valueToSet = null;
if (dbValue == DBNull.Value)//if value is null
valueToSet = map.DefaultValue;
else
valueToSet = dbValue;
//Set value in property
setValue<T>(instance, item, valueToSet);
}
}
}
return instance;
}
/// <summary>
/// Set "item" property.
/// </summary>
/// <typeparam name="T">Return entity type</typeparam>
/// <param name="instance">T type instance</param>
/// <param name="item">Property name to return value</param>
/// <param name="valueToSet">Value to set to the property</param>
private static void setValue<T>(T instance, PropertyInfo item, object valueToSet) where T : class, new()
{
if (valueToSet == null)
{
CultureInfo ci = CultureInfo.InvariantCulture;
if (item.PropertyType.IsSubclassOf(typeof(System.ValueType)))
{
//if is a value type and is nullable
if (item.PropertyType.FullName.Contains("System.Nullable"))
{
item.SetValue(instance, null, BindingFlags.Public, null, null, ci);
}
else
{
item.SetValue(instance, Activator.CreateInstance(item.PropertyType, null), BindingFlags.Public, null, null, ci);
}
}
else //property type is reference type
{
item.SetValue(instance, null, BindingFlags.Public, null, null, ci);
}
}
else // set not null value
{
//if is a value type and is nullable
if (item.PropertyType.FullName.Contains("System.Nullable"))
{
item.SetValue(instance, Convert.ChangeType(valueToSet, Nullable.GetUnderlyingType(item.PropertyType)), null);
}
else
{
item.SetValue(instance, Convert.ChangeType(valueToSet, item.PropertyType), null);
}
}
}
}
What I do here, in essence, is to map the domain entities with the database fields, and a data helper attacks the tables automatically. An example of one of these entities is:
public class ComboBox
{
/// <summary>
/// Represents a ComboBox item.
/// </summary>
[Mapping("CODE", DefaultValue = 0, DBType = DbParametersTypes.Varchar2, IsKey = true, IdentifierFK = "")]
public string Code { get; set; }
/// <summary>
/// Represents Text.
/// </summary>
[Mapping("DESCRIPTION", DefaultValue = "", DBType = DbParametersTypes.Varchar2, IsKey = false, IdentifierFK = "")]
public string Description { get; set; }
}
And the attribute class I use:
public sealed class MappingAttribute : Attribute
{
public string ColumnName { get; set; }
public object DefaultValue { get; set; }
public DbParametersTypes DBType { get; set; }
public bool IsKey { get; set; }
public string IdentifierFK { get; set; }
public bool IsParameter { get; set; }
public MappingAttribute(string columnName)
{
if (String.IsNullOrEmpty(columnName))
throw new ArgumentNullException("columnName");
ColumnName = columnName;
}
}
I read here that a possible improvement could be an expression tree, but, first, I'm not an expression tress expert, and second, I have to solve this with .NET 3.5...(in the sample .NET 4 or 4.5 is used...)
¿Suggestions?
Thanks in advance.
public static class DataRowExtensions
{
/// <summary>
/// Maps DataRow objecto to entity T depending on the defined attributes.
/// </summary>
/// <typeparam name="T">Entity to map.</typeparam>
/// <param name="rowInstance">DataRow instance.</param>
/// <returns>Instance to created entity.</returns>
public static T MapRow<T>( this DataRow rowInstance ) where T : class, new()
{
//Create T item
var instance = new T();
Mapper<T>.MapRow( instance, rowInstance );
return instance;
}
#region Nested type: Mapper
private static class Mapper<T>
where T : class
{
private static readonly ItemMapper[] __mappers;
static Mapper()
{
__mappers = typeof (T)
.GetProperties()
.Where( p => p.IsDefined( typeof (MappingAttribute), false ) )
.Select( p => new
{
Property = p,
Attribute = p
.GetCustomAttributes( typeof (MappingAttribute), false )
.Cast<MappingAttribute>()
.FirstOrDefault()
} )
.Select( m => new ItemMapper( m.Property, m.Attribute ) )
.ToArray();
}
public static void MapRow( T instance, DataRow row )
{
foreach ( var mapper in __mappers )
{
mapper.MapRow( instance, row );
}
}
#region Nested type: ItemMapper
private sealed class ItemMapper
{
private readonly MappingAttribute _attribute;
private readonly PropertyInfo _property;
public ItemMapper( PropertyInfo property, MappingAttribute attribute )
{
_property = property;
_attribute = attribute;
}
public void MapRow( T instance, DataRow rowInstance )
{
//TODO: Implement this with the code already provided
}
}
#endregion
}
#endregion
}
The first time the extension method is called for a given <T>, the static constructor will run and cache an instance Mapper for each property that has a MappingAttribute attached. Then, for every call after that, it will used the cached mappers to do the actual copy.
You can also make Mapper abstract, and use a different subclass for each branch in your setValue<T>(). That way most of your reflection only happens once.
My question is shown in this code
I have class like that
public class MainCS
{
public int A;
public int B;
public int C;
public int D;
}
public class Sub1
{
public int A;
public int B;
public int C;
}
public void MethodA(Sub1 model)
{
MainCS mdata = new MainCS() { A = model.A, B = model.B, C = model.C };
// is there a way to directly cast class Sub1 into MainCS like that
mdata = (MainCS) model;
}
Use JSON serialization and deserialization:
using Newtonsoft.Json;
Class1 obj1 = new Class1();
Class2 obj2 = JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(obj1));
Or:
public class Class1
{
public static explicit operator Class2(Class1 obj)
{
return JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(obj));
}
}
Which then allows you to do something like
Class1 obj1 = new Class1();
Class2 obj2 = (Class2)obj1;
You have already defined the conversion, you just need to take it one step further if you would like to be able to cast. For example:
public class sub1
{
public int a;
public int b;
public int c;
public static explicit operator maincs(sub1 obj)
{
maincs output = new maincs() { a = obj.a, b = obj.b, c = obj.c };
return output;
}
}
Which then allows you to do something like
static void Main()
{
sub1 mySub = new sub1();
maincs myMain = (maincs)mySub;
}
What he wants to say is:
"If you have two classes which share most of the same properties you can cast an object from class a to class b and automatically make the system understand the assignment via the shared property names?"
Option 1: Use reflection
Disadvantage : It's gonna slow you down more than you think.
Option 2: Make one class derive from another, the first one with common properties and other an extension of that.
Disadvantage: Coupled! if your're doing that for two layers in your application then the two layers will be coupled!
Let there be:
class customer
{
public string firstname { get; set; }
public string lastname { get; set; }
public int age { get; set; }
}
class employee
{
public string firstname { get; set; }
public int age { get; set; }
}
Now here is an extension for Object type:
public static T Cast<T>(this Object myobj)
{
Type objectType = myobj.GetType();
Type target = typeof(T);
var x = Activator.CreateInstance(target, false);
var z = from source in objectType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property select source ;
var d = from source in target.GetMembers().ToList()
where source.MemberType == MemberTypes.Property select source;
List<MemberInfo> members = d.Where(memberInfo => d.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
PropertyInfo propertyInfo;
object value;
foreach (var memberInfo in members)
{
propertyInfo = typeof(T).GetProperty(memberInfo.Name);
value = myobj.GetType().GetProperty(memberInfo.Name).GetValue(myobj,null);
propertyInfo.SetValue(x,value,null);
}
return (T)x;
}
Now you use it like this:
static void Main(string[] args)
{
var cus = new customer();
cus.firstname = "John";
cus.age = 3;
employee emp = cus.Cast<employee>();
}
Method cast checks common properties between two objects and does the assignment automatically.
You could change your class structure to:
public class maincs : sub1
{
public int d;
}
public class sub1
{
public int a;
public int b;
public int c;
}
Then you could keep a list of sub1 and cast some of them to mainc.
By using following code you can copy any class object to another class object for same name and same type of properties.
public class CopyClass
{
/// <summary>
/// Copy an object to destination object, only matching fields will be copied
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sourceObject">An object with matching fields of the destination object</param>
/// <param name="destObject">Destination object, must already be created</param>
public static void CopyObject<T>(object sourceObject, ref T destObject)
{
// If either the source, or destination is null, return
if (sourceObject == null || destObject == null)
return;
// Get the type of each object
Type sourceType = sourceObject.GetType();
Type targetType = destObject.GetType();
// Loop through the source properties
foreach (PropertyInfo p in sourceType.GetProperties())
{
// Get the matching property in the destination object
PropertyInfo targetObj = targetType.GetProperty(p.Name);
// If there is none, skip
if (targetObj == null)
continue;
// Set the value in the destination
targetObj.SetValue(destObject, p.GetValue(sourceObject, null), null);
}
}
}
Call Method Like,
ClassA objA = new ClassA();
ClassB objB = new ClassB();
CopyClass.CopyObject(objOfferMast, ref objB);
It will copy objA into objB.
You can provide an explicit overload for the cast operator:
public static explicit operator maincs(sub1 val)
{
var ret = new maincs() { a = val.a, b = val.b, c = val.c };
return ret;
}
Another option would be to use an interface that has the a, b, and c properties and implement the interface on both of the classes. Then just have the parameter type to methoda be the interface instead of the class.
Using this code you can copy any class object to another class object for same name and same type of properties.
JavaScriptSerializer JsonConvert = new JavaScriptSerializer();
string serializeString = JsonConvert.Serialize(objectEntity);
objectViewModel objVM = JsonConvert.Deserialize<objectViewModel>(serializeString);
There are some great answers here, I just wanted to add a little bit of type checking here as we cannot assume that if properties exist with the same name, that they are of the same type. Here is my offering, which extends on the previous, very excellent answer as I had a few little glitches with it.
In this version I have allowed for the consumer to specify fields to be excluded, and also by default to exclude any database / model specific related properties.
public static T Transform<T>(this object myobj, string excludeFields = null)
{
// Compose a list of unwanted members
if (string.IsNullOrWhiteSpace(excludeFields))
excludeFields = string.Empty;
excludeFields = !string.IsNullOrEmpty(excludeFields) ? excludeFields + "," : excludeFields;
excludeFields += $"{nameof(DBTable.ID)},{nameof(DBTable.InstanceID)},{nameof(AuditableBase.CreatedBy)},{nameof(AuditableBase.CreatedByID)},{nameof(AuditableBase.CreatedOn)}";
var objectType = myobj.GetType();
var targetType = typeof(T);
var targetInstance = Activator.CreateInstance(targetType, false);
// Find common members by name
var sourceMembers = from source in objectType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var targetMembers = from source in targetType.GetMembers().ToList()
where source.MemberType == MemberTypes.Property
select source;
var commonMembers = targetMembers.Where(memberInfo => sourceMembers.Select(c => c.Name)
.ToList().Contains(memberInfo.Name)).ToList();
// Remove unwanted members
commonMembers.RemoveWhere(x => x.Name.InList(excludeFields));
foreach (var memberInfo in commonMembers)
{
if (!((PropertyInfo)memberInfo).CanWrite) continue;
var targetProperty = typeof(T).GetProperty(memberInfo.Name);
if (targetProperty == null) continue;
var sourceProperty = myobj.GetType().GetProperty(memberInfo.Name);
if (sourceProperty == null) continue;
// Check source and target types are the same
if (sourceProperty.PropertyType.Name != targetProperty.PropertyType.Name) continue;
var value = myobj.GetType().GetProperty(memberInfo.Name)?.GetValue(myobj, null);
if (value == null) continue;
// Set the value
targetProperty.SetValue(targetInstance, value, null);
}
return (T)targetInstance;
}
I tried to use the Cast Extension (see https://stackoverflow.com/users/247402/stacker) in a situation where the Target Type contains a Property that is not present in the Source Type. It did not work, I'm not sure why. I refactored to the following extension that did work for my situation:
public static T Casting<T>(this Object source)
{
Type sourceType = source.GetType();
Type targetType = typeof(T);
var target = Activator.CreateInstance(targetType, false);
var sourceMembers = sourceType.GetMembers()
.Where(x => x.MemberType == MemberTypes.Property)
.ToList();
var targetMembers = targetType.GetMembers()
.Where(x => x.MemberType == MemberTypes.Property)
.ToList();
var members = targetMembers
.Where(x => sourceMembers
.Select(y => y.Name)
.Contains(x.Name));
PropertyInfo propertyInfo;
object value;
foreach (var memberInfo in members)
{
propertyInfo = typeof(T).GetProperty(memberInfo.Name);
value = source.GetType().GetProperty(memberInfo.Name).GetValue(source, null);
propertyInfo.SetValue(target, value, null);
}
return (T)target;
}
Note that I changed the name of the extension as the Name Cast conflicts with results from Linq. Hat tip https://stackoverflow.com/users/2093880/usefulbee
var obj = _account.Retrieve(Email, hash);
AccountInfoResponse accountInfoResponse = new AccountInfoResponse();
if (obj != null)
{
accountInfoResponse =
JsonConvert.
DeserializeObject<AccountInfoResponse>
(JsonConvert.SerializeObject(obj));
}
image description
I developed a Class ObjectChanger that contains the functions ConvertToJson, DeleteFromJson, AddToJson, and ConvertToObject. These functions can be used to convert a C# object to JSON which properties can then be removed or added accordingly. Afterwards the adjusted JSON object can simply be converted to a new object using ConvertToObject function. In the sample code below the class "AtoB" utilizes ObjectChanger in its GetAtoB() function:
using System.Collections.Generic;
using Newtonsoft.Json;
using Nancy.Json;
namespace YourNameSpace
{
public class A
{
public int num1 { get; set; }
public int num2 { get; set; }
public int num3 { get; set; }
}
public class B//remove num2 and add num4
{
public int num1 { get; set; }
public int num3 { get; set; }
public int num4 { get; set; }
}
/// <summary>
/// This class utilizes ObjectChanger to illustrate how
/// to convert object of type A to one of type B
/// by converting A to a Json Object, manipulating the JSON
/// and then converting it to object of type B
/// </summary>
public class AtoB
{
public dynamic GetAtoB()
{
A objectA = new A
{
num1 =1, num2 =2,num3 =3
};
//convert "objectA" to JSON object "jsonA"
dynamic jsonA = ObjectChanger.ConvertToJson(objectA);
//remove num2 from jsonA
ObjectChanger.DeleteFromJson(jsonA, "num2");
//add property num4 with value 4 to jsonA
ObjectChanger.AddToJson(jsonA, "num4", 4);
B objectB = ObjectChanger.ConvertToObject<B>(jsonA);
return objectB;
//note: Above DeleteFromJson not needed if the
//property(e.g "num2") doesn't exist in objectB
//the jsonA will still keep the num2 but when
//ConvertToObject is called the objectB will only get
//populated with the relevant fields.
}
}
public class ObjectChanger
{
/// <summary>
/// Converts a provided class to JsonObject
/// sample use: dynamic r = ObjectChanger.ConvertToJson(providedObj);
/// </summary>
public static dynamic ConvertToJson(dynamic providedObj)
{
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
//https://stackoverflow.com/questions/7397207/json-net-error-self-referencing-loop-detected-for-type
return JsonConvert.DeserializeObject<System.Dynamic.ExpandoObject>
(JsonConvert.SerializeObject(providedObj,jss));
}
/// <summary>
/// Deletes Property from Json Object
/// sample use: dynamic r = ObjectChanger.ConvertToJson(providedObj);
/// ((IDictionary<string, object>)r).Remove("keyvalue");
/// </summary>
public static dynamic DeleteFromJson(dynamic providedObj, string keyvalue)
{
((IDictionary<string, object>)providedObj).Remove(keyvalue);
return providedObj;
}
/// <summary>
/// Adds Property to provided Json Object
/// </summary>
/// <param name="providedObj"></param>
/// <param name="key"></param>
/// <param name="keyvalue"></param>
/// <returns>Returns updated Object</returns>
public static dynamic AddToJson(dynamic providedObj, string key,
dynamic keyvalue)
{
((IDictionary<string, object>)providedObj).Add(key, keyvalue);
return providedObj;
}
/// <summary>
/// Converts provided object providedObj
/// to an object of type T
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="providedObj"></param>
/// <returns></returns>
public static T ConvertToObject<T>(dynamic providedObj)
{
var serializer = new JavaScriptSerializer();
var json = serializer.Serialize(providedObj);
var c = serializer.Deserialize<T>(json);
return c;
}
}
}