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.
Related
I have two models
public class NewrecordModel
{
public string NewName { get; set; }
public string NewFileName { get; set; }
public string NewFileVersion { get; set; }
}
public class OldrecordModel
{
public string OldName { get; set; }
public string OldFileName { get; set; }
public string OldFileVersion { get; set; }
}
I need to compare the property, NewName with OldName and NewFileVersion with OldFileVersion and return the difference from NewrecordModel in a list.
I tried the below one,
var unMatchedRecord = NewrecordModel.Where(o => !OldrecordModel.Any(n => n.OldName == o.NewName) || !OldrecordModel.Any(n => n.OldFileVersion == o.NewFileVersion));
The above one is returning the unmatch NewrecordModel in a list and it is working fine, but i need to compare OldFileVersion is less than NewFileVersion and return the list now. While using OldFileVersion is less than NewFileVersion in query it is listing unrelated data as output.
Below is the query i tried to compare,
var unMatchedVersion = NewrecordModel.Where(o => OldrecordModel.Any(n => n.OldFileVersion.ToInt() < o.NewFileVersion.ToInt()));
Is the above linq is correct. How to compare the numbers in linq and return the result.
I wrote this for get properties name and value what changed. I hope this help you.
public static class CompareObject
{
/// <summary>
/// Compares the properties of two objects of the same type and returns if all properties are equal.
/// </summary>
/// <param name="objectA">The first object to compare.</param>
/// <param name="objectB">The second object to compre.</param>
/// <param name="ignoreList">A list of property names to ignore from the comparison.</param>
/// <returns><c>true</c> if all property values are equal, otherwise <c>false</c>.</returns>
public static Dictionary<string,object> AreObjectsEqual(object objectA, object objectB, params string[] ignoreList)
{
//bool result;
var list = new Dictionary<string, object>();
if (objectA != null && objectB != null)
{
Type objectType;
objectType = objectA.GetType();
//result = true; // assume by default they are equal
foreach (PropertyInfo propertyInfo in objectType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && !ignoreList.Contains(p.Name)))
{
// if it is a primative type, value type or implements IComparable, just directly try and compare the value
if (CanDirectlyCompare(propertyInfo.PropertyType))
{
object valueA;
object valueB;
valueA = propertyInfo.GetValue(objectA, null);
valueB = propertyInfo.GetValue(objectB, null);
if (!AreValuesEqual(valueA, valueB))
{
list.Add(propertyInfo.Name, valueA);
//Console.WriteLine("Mismatch with property '{0}.{1}' found.", objectType.FullName, propertyInfo.Name);
//result = false;
}
}
}
}
//else
// result = object.Equals(objectA, objectB);
return list;
}
/// <summary>
/// Determines whether value instances of the specified type can be directly compared.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>
/// <c>true</c> if this value instances of the specified type can be directly compared; otherwise, <c>false</c>.
/// </returns>
private static bool CanDirectlyCompare(Type type)
{
return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
/// <summary>
/// Compares two values and returns if they are the same.
/// </summary>
/// <param name="valueA">The first value to compare.</param>
/// <param name="valueB">The second value to compare.</param>
/// <returns><c>true</c> if both values match, otherwise <c>false</c>.</returns>
private static bool AreValuesEqual(object valueA, object valueB)
{
bool result;
IComparable selfValueComparer;
selfValueComparer = valueA as IComparable;
if (valueA == null && valueB != null || valueA != null && valueB == null)
result = false; // one of the values is null
else if (selfValueComparer != null && selfValueComparer.CompareTo(valueB) != 0)
result = false; // the comparison using IComparable failed
else if (!object.Equals(valueA, valueB))
result = false; // the comparison using Equals failed
else
result = true; // match
return result;
}
}
and to use
var changedColumns = CompareObject.AreObjectsEqual(NewrecordModel, OldrecordModel);
It might be an object from NewrecordModel collection, for which there are no objects in OldrecordModel collection with the same name OR any identically named object from OldrecordModel collection has a smaller OldFileVersion
Check the codes below:
var unMatchedVersion =
newrecordModel.Where(o => !oldrecordModel.Any(n => n.OldName == o.NewName) ||
oldrecordModel.Any(n => n.OldName == o.NewName &&
int.Parse(n.OldFileVersion) < int.Parse(o.NewFileVersion)));
I have the following custom attribute, which can be applied on properties:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class IdentifierAttribute : Attribute
{
}
For example:
public class MyClass
{
[Identifier()]
public string Name { get; set; }
public int SomeNumber { get; set; }
public string SomeOtherProperty { get; set; }
}
There will also be other classes, to which the Identifier attribute could be added to properties of different type:
public class MyOtherClass
{
public string Name { get; set; }
[Identifier()]
public int SomeNumber { get; set; }
public string SomeOtherProperty { get; set; }
}
I then need to be able to get this information in my consuming class.
For example:
public class TestClass<T>
{
public void GetIDForPassedInObject(T obj)
{
var type = obj.GetType();
//type.GetCustomAttributes(true)???
}
}
What's the best way of going about this?
I need to get the type of the [Identifier()] field (int, string, etc...) and the actual value, obviously based on the type.
Something like the following,, this will use only the first property it comes accross that has the attribute, of course you could place it on more than one..
public object GetIDForPassedInObject(T obj)
{
var prop = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.FirstOrDefault(p => p.GetCustomAttributes(typeof(IdentifierAttribute), false).Count() ==1);
object ret = prop !=null ? prop.GetValue(obj, null) : null;
return ret;
}
public class TestClass<T>
{
public void GetIDForPassedInObject(T obj)
{
PropertyInfo[] properties =
obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
PropertyInfo IdProperty = (from PropertyInfo property in properties
where property.GetCustomAttributes(typeof(Identifier), true).Length > 0
select property).First();
if(null == IdProperty)
throw new ArgumentException("obj does not have Identifier.");
Object propValue = IdProperty.GetValue(entity, null)
}
}
A bit late but here is something I did for enums (could be any object also) and getting the description attribute value using an extension (this could be a generic for any attribute):
public enum TransactionTypeEnum
{
[Description("Text here!")]
DROP = 1,
[Description("More text here!")]
PICKUP = 2,
...
}
Getting the value:
var code = TransactionTypeEnum.DROP.ToCode();
Extension supporting all my enums:
public static string ToCode(this TransactionTypeEnum val)
{
return GetCode(val);
}
public static string ToCode(this DockStatusEnum val)
{
return GetCode(val);
}
public static string ToCode(this TrailerStatusEnum val)
{
return GetCode(val);
}
public static string ToCode(this DockTrailerStatusEnum val)
{
return GetCode(val);
}
public static string ToCode(this EncodingType val)
{
return GetCode(val);
}
private static string GetCode(object val)
{
var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : string.Empty;
}
Here is a more real-word example. We use an extension method and check if a property contains a FieldMetaDataAttribute (a custom attribute in my source code base)
with valid Major and MinorVersion. What is of general interest is the part where we use the parent class type and GetProperties and retrieve the ProperyInfo and then use GetCustomAttribute to retrieve a attribute FieldMetaDataAttribute in this special case. Use this code for inspiration how to do more generic way of retrieving a custom attribute. This can of course be polished to make a general method to retrieve a given attribute of any property of a class instance.
/// <summary>
/// Executes the action if not the field is deprecated
/// </summary>
/// <typeparam name="TProperty"></typeparam>
/// <typeparam name="TForm"></typeparam>
/// <param name="form"></param>
/// <param name="memberExpression"></param>
/// <param name="actionToPerform"></param>
/// <returns>True if the action was performed</returns>
public static bool ExecuteActionIfNotDeprecated<TForm, TProperty>(this TForm form, Expression<Func<TForm, TProperty>> memberExpression, Action actionToPerform)
{
var memberExpressionConverted = memberExpression.Body as MemberExpression;
if (memberExpressionConverted == null)
return false;
string memberName = memberExpressionConverted.Member.Name;
PropertyInfo matchingProperty = typeof(TForm).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.FirstOrDefault(p => p.Name == memberName);
if (matchingProperty == null)
return false; //should not occur
var fieldMeta = matchingProperty.GetCustomAttribute(typeof(FieldMetadataAttribute), true) as FieldMetadataAttribute;
if (fieldMeta == null)
{
actionToPerform();
return true;
}
var formConverted = form as FormDataContract;
if (formConverted == null)
return false;
if (fieldMeta.DeprecatedFromMajorVersion > 0 && formConverted.MajorVersion > fieldMeta.DeprecatedFromMajorVersion)
{
//major version of formConverted is deprecated for this field - do not execute action
return false;
}
if (fieldMeta.DeprecatedFromMinorVersion > 0 && fieldMeta.DeprecatedFromMajorVersion > 0
&& formConverted.MinorVersion >= fieldMeta.DeprecatedFromMinorVersion
&& formConverted.MajorVersion >= fieldMeta.DeprecatedFromMajorVersion)
return false; //the field is expired - do not invoke action
actionToPerform();
return true;
}
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'm trying to create a class instance from my Linq query result. Since I have to do this for many classes, I'm trying to find the most suitable shortcut. I'm wondering whether I can make the "select" part of the query any shorter.
My class:
public class current_control_id_class
{
public string asset_class { get; set; }
public string region_code { get; set; }
public string instance_code { get; set; }
public int sdi_control_id { get; set; }
public int rows_loaded { get; set; }
}
My assignment function:
foreach (var results in query)
{
foreach (PropertyInfo result in results.GetType().GetProperties())
{
string name = result.Name;
foreach (PropertyInfo info in used.GetType().GetProperties())
{
if (result.Name == info.Name)
{
Console.WriteLine("Result {0} matches class {1} and the value is {2}", result.Name, info.Name, result.GetValue(results,null));
}
}
}
}
My query (i know this works)
current_control_id_class used = new current_control_id_class();
var query =
from result in t_sdi_current_control_id.AsQueryable()
where result.asset_class == asset_class
&& result.region_code == region
&& result.instance_code == site
select new current_control_id_class() {
rows_loaded = result.rows_loaded,
sdi_control_id = result.sdi_control_id,
asset_class = result.asset_class,
hsbc_region_code = result.hsbc_region_code,
hsbc_instance_code = result.hsbc_instance_code
};
You might be able to use AutoMapper to map instances of t_sdi_current_control_id to instances of current_control_id_class:
First initialise the mapping:
Mapper.CreateMap<t_sdi_current_control_id, current_control_id_class>();
Then use it:
var query =
from result in t_sdi_current_control_id.AsQueryable()
where result.asset_class == asset_class
&& result.region_code == region
&& result.instance_code == site
select Mapper.Map<current_control_id_class>(result);
If you don't want to use a third-party library, here's some tested code to do it for you:
/// <summary>
/// Maps instances of <typeparam name="TSource"/> to new instances of
/// <typeparam name="TDestination"/> by copying across accessible public
/// instance properties whose names match.
/// </summary>
/// <remarks>
/// Internally uses a compiled Expression, so mapping should be quick at
/// the expense of <see cref="Mapper"/> initialisation.
/// </remarks>
public class Mapper<TSource, TDestination>
where TDestination : new()
{
readonly Func<TSource, TDestination> map;
public Mapper()
{
this.map = GenerateMapping();
}
static Func<TSource, TDestination> GenerateMapping()
{
var sourceProperties = GetPublicInstancePropertiesWithAccessors<TSource>(property => property.GetGetMethod());
var destinationProperties = GetPublicInstancePropertiesWithAccessors<TDestination>(property => property.GetSetMethod());
var source = Expression.Parameter(typeof(TSource));
var destination = Expression.Variable(typeof(TDestination));
var copyPropertyValues = from sourceProperty in sourceProperties
from destinationProperty in destinationProperties
where sourceProperty.Name.Equals(destinationProperty.Name, StringComparison.Ordinal)
select Expression.Assign(
Expression.Property(destination, destinationProperty),
Expression.Property(source, sourceProperty)
);
var variables = new[] { destination };
var assignNewDestinationInstance = Expression.Assign(destination, Expression.New(typeof(TDestination)));
var returnDestinationInstance = new Expression[] { destination };
var statements =
new[] { assignNewDestinationInstance }
.Concat(copyPropertyValues)
.Concat(returnDestinationInstance);
var body = Expression.Block(variables, statements);
var parameters = new[] { source };
var method = Expression.Lambda<Func<TSource, TDestination>>(body, parameters);
return method.Compile();
}
/// <summary>
/// Gets public instance properties of <typeparamref name="T"/> that
/// have accessible accessors defined by <paramref name="getAccessor"/>.
/// </summary>
static IEnumerable<PropertyInfo> GetPublicInstancePropertiesWithAccessors<T>(Func<PropertyInfo, MethodInfo> getAccessor)
{
var type = typeof(T);
var publicInstanceProperties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
return from property in publicInstanceProperties
let accessor = getAccessor(property)
where accessor != null
select property;
}
public TDestination Map(TSource source)
{
return map(source);
}
}
Use it like this:
//Keep this around so it gets re-used.
var mapper = new Mapper<t_sdi_current_control_id, current_control_id_class>();
var result = mapper.Map(value);
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;
}
}
}