I have multiple DTO class which require type converter. The following is one of the implementations. As you will see, I need ConvertFrom only.
public class EmployeeFilterTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (typeof(string) == sourceType)
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var strVal = value as String;
if (string.IsNullOrEmpty(strVal))
return new EmployeeFilter();
EmployeeFilter employeeFilter = new EmployeeFilter();
string[] filters = strVal.Split(';');
foreach (var filter in filters)
{
var filterSplit = filter.Split(':');
if (filterSplit.Length == 2)
{
var key = filterSplit[0];
var val = filterSplit[1];
SetPropertyValue(employeeFilter, key, val);
}
}
return employeeFilter;
}
private void SetPropertyValue(EmployeeFilter employeeFilter, string key, string val)
{
var t = typeof(EmployeeFilter);
PropertyInfo[] props = t.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropertyInfo prop = props.Where(p => p.Name.Equals(key, StringComparison.CurrentCultureIgnoreCase) == true && p.CanWrite).FirstOrDefault();
if (prop != null)
prop.SetValue(employeeFilter, val);
}
}
I want to make multiple DTOs sharing the same converter in hopes of reducing code duplication as well as tests and after some researches, I have 2 problems at hand
Get the type that I want to convert in ConvertFrom method
Using Type class to initialize new object
For the first one, I don't know how to get from ITypeDescriptorContext.
For the second one, I will use the following according to this post
Type employeeType = typeof(EmployeeFilter);
object objtype = Activator.CreateInstance(employeeType);
So, how to get the type that I want to convert to?
Test case
public class testConverter
{
[Theory]
[InlineData(typeof(string), true)]
[InlineData(typeof(int), false)]
public void testCanConvertFrom(Type sourceType, bool expected)
{
//Arrange
Type randomType = typeof(Book);
Type typeGenericConverter = typeof(EmployeeFilterTypeConverter<>);
Type typeActualConverter = typeGenericConverter.MakeGenericType(randomType);
/*
1. The way of creating EmployeeFilterTypeConverter<thattype>
https://stackoverflow.com/a/266282
*/
dynamic testConverter = Activator.CreateInstance(typeActualConverter);
Mock<ITypeDescriptorContext> mockDescContext = new Mock<ITypeDescriptorContext>();
//Act
bool actual = testConverter.CanConvertFrom(mockDescContext.Object, sourceType);
//Assert
Assert.Equal(expected, actual);
}
[Theory, ClassData(typeof(TestConvertFromType1))]
/*
1. All these classdata, propertydata stuff just for passing complex objects to test
https://stackoverflow.com/a/22093968
*/
public void testConverFromType1(object value, EmployeeFilter expected)
{
//api/employee?filter=firstName:Nikhil;lastName:Doomra
//Arrange
EmployeeFilterTypeConverter<EmployeeFilter> testConverter = new EmployeeFilterTypeConverter<EmployeeFilter>();
Mock<ITypeDescriptorContext> mockDescContext = new Mock<ITypeDescriptorContext>();
//Act
EmployeeFilter actual = testConverter.ConvertFrom(mockDescContext.Object, null, value) as EmployeeFilter;
//Assert
//public static void Equal<T>(T expected, T actual);
Assert.Equal(expected, actual);
}
[Theory, ClassData(typeof(TestConvertFromType2))]
public void testConverFromType2(object value, GeoPoint expected)
{
//api/employee?filter=firstName:Nikhil;lastName:Doomra
//Arrange
EmployeeFilterTypeConverter<GeoPoint> testConverter = new EmployeeFilterTypeConverter<GeoPoint>();
Mock<ITypeDescriptorContext> mockDescContext = new Mock<ITypeDescriptorContext>();
//Act
GeoPoint actual = testConverter.ConvertFrom(mockDescContext.Object, null, value) as GeoPoint;
//Assert
//public static void Equal<T>(T expected, T actual);
Assert.Equal(expected, actual);
}
}
Test Data Model
public class TestConvertFromType1: IEnumerable<object[]>
{
private readonly List<object[]> _data = new List<object[]>
{
new object[] { "firstName:Nikhil;lastName:Doomra",
new EmployeeFilter {
FirstName = "Nikhil", LastName = "Doomra"
}},
new object[] { "firstName:Nikhil",
new EmployeeFilter {
FirstName = "Nikhil"
}}
};
public IEnumerator<object[]> GetEnumerator()
{ return _data.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return GetEnumerator(); }
}
public class TestConvertFromType2 : IEnumerable<object[]>
{
private readonly List<object[]> _data = new List<object[]>
{
new object[] { "Latitude:12.345;Longitude:342.12",
new GeoPoint {
Latitude = 12.345, Longitude = 342.12
}},
new object[] { "Latitude:11.234;Longitude:345.12",
new GeoPoint {
Latitude = 11.234, Longitude = 345.12
}}
};
public IEnumerator<object[]> GetEnumerator()
{ return _data.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return GetEnumerator(); }
}
Generic Converter
public class EmployeeFilterTypeConverter<T> : TypeConverter where T: new()
/*
1. You can't declare T type = new T() without this constraint
Evidently it is because compiler can't say what is the type!
https://stackoverflow.com/a/29345294
*/
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (typeof(string) == sourceType)
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
var strVal = value as String;
if (string.IsNullOrEmpty(strVal))
return new EmployeeFilter();
T converTo = new T();
string[] filters = strVal.Split(';');
foreach (var filter in filters)
{
string[] filterSplit = filter.Split(':');
if (filterSplit.Length == 2)
{
string key = filterSplit[0];
string val = filterSplit[1];
SetPropertyValue(converTo, key, val);
}
}
return converTo;
}
private void SetPropertyValue(T converTo, string key, string val)
{
Type t = typeof(T);
PropertyInfo[] props = t.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropertyInfo prop = props.Where(p => p.Name.Equals(key, StringComparison.CurrentCultureIgnoreCase) == true && p.CanWrite).FirstOrDefault();
if (prop is null) return;
prop.SetValue(converTo, TypeDescriptor.GetConverter(prop.PropertyType).ConvertFrom(val));
/*
1. Problem: val is a string and if your target property is non-string there is
a contradiction.
The following link offers a solution
https://stackoverflow.com/a/2380483
*/
}
}
EmployeeFilter
[TypeConverter(typeof(EmployeeFilterTypeConverter<EmployeeFilter>))]
public class EmployeeFilter: IEquatable<EmployeeFilter>
{
/*
1. As you can see, the DTO omitted the
a. ID
b. DOB
*/
public string FirstName { get; set; }
public string LastName { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public DateTime? DOJ { get; set; }
public bool Equals(EmployeeFilter other)
{
/*
1. You need to put parenthesis around (this.FirstName == other.FirstName)
2. https://learn.microsoft.com/en-us/dotnet/api/system.iequatable-1?view=net-5.0
*/
return (this.FirstName == other.FirstName) &&
(this.LastName == other.LastName) &&
(this.Street == other.Street) &&
(this.City == other.City) &&
(this.State == other.State) &&
(this.ZipCode == other.ZipCode) &&
(this.DOJ == other.DOJ);
}
}
GeoPoint
public class GeoPoint: IEquatable<GeoPoint>
{
public double Latitude { get; set; }
public double Longitude { get; set; }
public static bool TryParse(string s, out GeoPoint result)
{
result = null;
var parts = s.Split(',');
if (parts.Length != 2)
{
return false;
}
double latitude, longitude;
if (double.TryParse(parts[0], out latitude) &&
double.TryParse(parts[1], out longitude))
{
result = new GeoPoint() { Longitude = longitude, Latitude = latitude };
return true;
}
return false;
}
public bool Equals(GeoPoint other)
{
return (this.Latitude == other.Latitude) && (this.Longitude == other.Longitude);
}
Edit: Added model classes
Related
Is there a way to do something like "string.Compare()", but for generic types. I want to check the range of some property values.
Here is what I am doing as a work around, but it is pretty ugly:
public class SomeClass<T>
{
public T MinValue { get; set; }
public T MaxValue { get; set; }
private T _value;
public T Value
{
get { return _value; }
set
{
_value = value;
// Restrict range to Min/Max
if (Comparer<T>.Default.Compare(MaxValue, value) < 0)
_value = MaxValue;
if (Comparer<T>.Default.Compare(MinValue, value) > 0)
_value = MinValue;
}
}
}
This code demonstates what I was talking about in my comment. Of course you will have to modify it to fit with your precise paradigm, of using it in a comparer, but this should be clear enough...
public class Program
{
public static void Main(string[] args)
{
System.Console.WriteLine("Hello World!");
TestObject testObject = new TestObject(15);
TestObject testObject2 = new TestObject(9);
TestObject testObject3 = new TestObject(31);
System.Console.ReadLine();
}
}
public class TestObject
{
[ValidateIntMin(Min = 10)]
[ValidateIntMax(30)]
public int SomeInt { get; set; }
public TestObject(int value)
{
SomeInt = value;
if (!Validator.Validate(this))
{
System.Console.WriteLine("Invalid Value assigned: " + value);
}
else
{
System.Console.WriteLine("" + SomeInt + " was a valid value");
}
}
}
public class ValidateIntMax : Attribute
{
public int Max { get; set; }
public ValidateIntMax(int MaxValue)
{
Max = MaxValue;
}
}
public class ValidateIntMin: Attribute
{
public int Min { get; set; }
}
public static class Validator
{
public static bool Validate<T>(T input) {
var attrType = typeof(T);
var properties = attrType.GetProperties();
bool isValid = true;
foreach (PropertyInfo propertyInfo in properties)
{
var customerMaxValueInt = propertyInfo.GetCustomAttributes(typeof(ValidateIntMax), false).FirstOrDefault();
var customerMinValueInt = propertyInfo.GetCustomAttributes(typeof(ValidateIntMin), false).FirstOrDefault();
if (customerMaxValueInt != null)
{
if (propertyInfo.PropertyType == typeof(int))
{
var currentPropertyInfoBeingTested = (int)propertyInfo.GetValue(input);
var currentMaxValueToVerifyAgainst = ((ValidateIntMax)customerMaxValueInt).Max;
if (currentPropertyInfoBeingTested > currentMaxValueToVerifyAgainst)
{
isValid = false;
}
}
}
if (customerMinValueInt != null)
{
if (propertyInfo.PropertyType == typeof(int))
{
var currentPropertyInfoBeingTested = (int)propertyInfo.GetValue(input);
var currentMaxValueToVerifyAgainst = ((ValidateIntMin)customerMinValueInt).Min;
if (currentPropertyInfoBeingTested < currentMaxValueToVerifyAgainst)
{
isValid = false;
}
}
}
}
return isValid;
}
}
Should give the output:
Hello World!
15 was a valid value
Invalid Value assigned: 9
Invalid Value assigned: 31
Of course you can add validation for different types, etc.
This is just to show a totally custom way of setting up your attributes.
I recommend you read up on the ValidationAttribute however, to see if you can't use the implemented functionality.
But this is just a PoC piece.
I'm looking for a way to get the name and value of a proptery in a POCO object. I've tried many solutions but can't seem to get them to work. I really liked this older solution but it causes a null ref error.
Here's kind of what I'm trying to do:
public class POCO
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Description { get; set; }
}
public class POCOValidationResult
{
public bool IsValid { get; set; }
public string Error { get; set; }
}
public abstract class Validator<T> where T : class
{
public T Entity { get; set; }
public abstract POCOValidationResult Validate();
protected POCOValidationResult ValidateStringPropertyToLengthOf(Expression<Func<T, object>> expression, int maxLength)
{
var propertyName = getPropertyName(expression);
var propertyValue = getPropertyValue(expression);
if (propertyValue.Length > maxLength)
{
return new POCOValidationResult()
{
Error = string.Format("{0} value is too long. Must be less or equal to {1}", propertyName, maxLength.ToString())
};
}
return new POCOValidationResult() { IsValid = true };
}
internal string getPropertyName(Expression<Func<T, object>> expression)
{
var memberExpersion = (MemberExpression)expression.Body;
return memberExpersion.Member.Name;
}
internal string getPropertyValue<R>(Expression<Func<T, R>> expression)
{
//struggling to get this to work
var me = (MemberExpression)expression.Body; // (MemberExpression)((MemberExpression)expression.Body).Expression;
var ce = (ConstantExpression)me.Expression; // Error here!
var fieldInfo = ce.Value.GetType().GetField(me.Member.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var value = fieldInfo.GetValue(ce.Value);
}
}
public class POCOValidator : Validator<POCO>
{
public override POCOValidationResult Validate()
{
var surnameValidationResult = ValidateStringPropertyToLengthOf(p => p.Surname, 10);
if (!surnameValidationResult.IsValid)
return surnameValidationResult;
//var descriptionValidationResult = ValidateStringPropertyToLengthOf(p => p.Description, 100);
//if (!descriptionValidationResult.IsValid)
// return descriptionValidationResult;
//var nameValidationResult = ValidateStringPropertyToLengthOf(p => p.Name, 15);
//if (!nameValidationResult.IsValid)
// return nameValidationResult;
return new POCOValidationResult() { IsValid = true };
}
}
public class WorkerBee
{
public void ImDoingWorkReally()
{
var pocoVallidation = new POCOValidator()
{
Entity = new POCO()
{
ID = 1,
Name = "James",
Surname = "Dean",
Description = "I'm not 007!"
}
};
var vallidationResult = pocoVallidation.Validate();
if (!vallidationResult.IsValid)
{
return;
}
//continue to do work...
}
}
class Program
{
static void Main(string[] args)
{
var workerBee = new WorkerBee();
workerBee.ImDoingWorkReally();
}
}
So as you can see, I'm trying to get the Property's name and value [by using an Expression (p => p.Surname) as a parameter in the method ValidateStringPropertyToLengthOf(...)]. The problem is that I'm getting a null ref error in getPropertyValue(Expression<Func<T, object>> expression) when it calls var ce = (ConstantExpression)me.Expression;
So does anyone have ideas on how to get this to work?
Thanks for taking the time to look into this. I really appreciate it and hope that my question also is helpful for others as I think this can be rather useful if I can get this to work.
EDIT: I've made the change as mentioned below in the comments and still getting the error "Unable to cast object of type 'System.Linq.Expressions.TypedParameterExpression' to type 'System.Linq.Expressions.ConstantExpression" when I run my unit test.
I worked out a solution (unfortunately the comments were not helpful). Here's the code that works:
public class POCO
{
public int ID { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Description { get; set; }
}
public class POCOValidationResult
{
public bool IsValid { get; set; }
public string Error { get; set; }
}
public abstract class Validator<T> where T : class
{
public T Entity { get; set; }
public abstract POCOValidationResult Validate();
protected POCOValidationResult ValidateStringPropertyToLengthOf(Expression<Func<T, object>> expression, int maxLength)
{
var propertyName = getPropertyName(expression);
var propertyValue = getPropertyValue(expression);
if (propertyValue.Length > maxLength)
{
return new POCOValidationResult()
{
Error = string.Format("{0} value is too long. Must be less or equal to {1}", propertyName, maxLength.ToString())
};
}
return new POCOValidationResult() { IsValid = true };
}
internal string getPropertyName(Expression<Func<T, object>> expression)
{
var memberExpersion = (MemberExpression)expression.Body;
return memberExpersion.Member.Name;
}
internal string getPropertyValue(Expression<Func<T, object>> expression)
{
var memberExpression = expression.Body as MemberExpression;
var propertyInfo = memberExpression.Member as PropertyInfo;
return propertyInfo.GetValue(Entity, null).ToString();
}
}
public class POCOValidator : Validator<POCO>
{
public override POCOValidationResult Validate()
{
var surnameValidationResult = ValidateStringPropertyToLengthOf(p => p.Surname, 10);
if (!surnameValidationResult.IsValid)
return surnameValidationResult;
var descriptionValidationResult = ValidateStringPropertyToLengthOf(p => p.Description, 100);
if (!descriptionValidationResult.IsValid)
return descriptionValidationResult;
var nameValidationResult = ValidateStringPropertyToLengthOf(p => p.Name, 15);
if (!nameValidationResult.IsValid)
return nameValidationResult;
return new POCOValidationResult() { IsValid = true };
}
}
public class WorkerBee
{
public void ImDoingWorkReally()
{
var pocoVallidation = new POCOValidator()
{
Entity = new POCO()
{
ID = 1,
Name = "James",
Surname = "Dean",
Description = "I'm not 007!"
}
};
var vallidationResult = pocoVallidation.Validate();
if (!vallidationResult.IsValid)
{
return;
}
//continue to do work...
}
}
class Program
{
static void Main(string[] args)
{
var workerBee = new WorkerBee();
workerBee.ImDoingWorkReally();
}
}
Note the change for getPropertyValue:
internal string getPropertyValue(Expression<Func<T, object>> expression)
{
var memberExpression = expression.Body as MemberExpression;
var propertyInfo = memberExpression.Member as PropertyInfo;
return propertyInfo.GetValue(Entity, null).ToString();
}
I noticed that MaudDib's getPropertyValue method only works for properties directly on the object. e.g. it will work for myObj.Value, but not for myObj.Something.Value. The following works for both. (I don't know if there's a better way, though).
Usage: var myValue = GetPropertyValue(myObj, o => o.Something.Value);
public static object GetPropertyValue<T>(T obj, Expression<Func<T, object>> expression)
{
var members = new CompositeExpressionVisitor().GetMembers(expression);
object currentVal = obj;
foreach (var part in members)
{
currentVal = GetPropertyValue(currentVal, part);
}
return currentVal;
}
private static object GetPropertyValue(object obj, MemberInfo member)
{
var propertyInfo = (PropertyInfo)member;
return propertyInfo.GetValue(obj, null);
}
private class CompositeExpressionVisitor : ExpressionVisitor
{
private readonly List<MemberInfo> _members = new List<MemberInfo>();
protected override Expression VisitMember(MemberExpression node)
{
_members.Add(node.Member);
return base.VisitMember(node);
}
public IReadOnlyCollection<MemberInfo> GetMembers(Expression e)
{
Visit(e is LambdaExpression expression ? expression.Body : e);
_members.Reverse();
return _members;
}
}
And, if you want the full path of the expression, e.g. you don't just want the leaf... you can do this:
Usage: var myValue = NameOf(() => myObj.Something.Value);
Returns: myObj.Something.Value
Or: var myValue = NameOf(() => myObj.Something.Value, 1);
Returns: Something.Value
public static string NameOf(Expression<Func<object>> expression, int startIndex = 0)
{
return new CompositeExpressionVisitor().GetPath(expression, startIndex);
}
private class CompositeExpressionVisitor : ExpressionVisitor
{
private readonly List<string> _parts = new List<string>();
protected override Expression VisitMember(MemberExpression node)
{
_parts.Add(node.Member.Name);
return base.VisitMember(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
_parts.Add(node.Name);
return base.VisitParameter(node);
}
public string GetPath(Expression e, int startIndex = 0)
{
Visit(e is LambdaExpression expression ? expression.Body : e);
_parts.Reverse();
return string.Join(".", _parts.Skip(startIndex));
}
}
Normally when I want for example to find the first or default item of a List I use this way:
myList.SingleOrDefault(x=>x.MyPropery01 == "myCondition");
However, I would like to know if it is possible, for example by reflection, if I set the the property MyProperty dynamically, something like:
myList.SingleOrDefault(x=>x.GetType().GetProperty("MyProperty01") == "myCondition");
Because sometimes I need to search for MyProperty01, sometimes for MyProperty02, MyProperty03, etc..
EDIT: in visual studio I get this error:
"Operator '==' can't be applied to operands of type System.reflection.PropertyInfo and string".
Yeah you can do that. You were pretty close, here is a demo you can drop in linqpad. Note that the important part is
Single(l => l.GetType().GetProperty(prop).GetValue(l).ToString() == "Condition")
void Main()
{
var myList = Enumerable.Range(0,10).Select(i => new Xmas(i,"name"+i)).ToList();
string prop = "name";
Console.WriteLine(myList.Single(l => l.GetType().GetProperty(prop).GetValue(l).ToString() == "name6").name);
}
public class Xmas
{
public int id { get; set; }
public string name { get; set; }
public Xmas( int id, string name )
{
this.id = id;
this.name = name;
}
}
Working example:
public class Apple
{
public string Color { get; set; }
}
public List<Apple> ApplesList {get;set;}
public void Process()
{
PropertyInfo pi = typeof(Apple).GetProperty("Color");
ApplesList = ApplesList.Where(r => (string)pi.GetValue(r) == "Red").ToList();
}
You could also write an Extension method, that allow to get the property on every object, returning null when it doesn't exist, or doesn't have a GetMethod. You could keep a Cache if you want
public static class ObjectExtension
{
static IDictionary<KeyValuePair<Type, string>, PropertyInfo> propertyCache = new Dictionary<KeyValuePair<Type, string>, PropertyInfo>();
public static object GetProperty(this object source, string propertyName, bool useCache = true)
{
if (source == null)
{
return null;
}
var sourceType = source.GetType();
KeyValuePair<Type, string> kvp = new KeyValuePair<Type, string>(sourceType, propertyName);
PropertyInfo property = null;
if (!useCache || !propertyCache.ContainsKey(kvp))
{
property = sourceType.GetProperty(propertyName);
if (property == null)
{
return null;
}
var get = property.GetGetMethod();
if (get == null)
{
return null;
}
if (useCache)
{
propertyCache.Add(kvp, property);
}
}
else
{
property = propertyCache[kvp];
}
return property.GetValue(source, null);
}
public static T GetProperty<T>(this object source, string propertyName)
{
object value = GetProperty((object)source, propertyName);
if (value == null)
{
return default(T);
}
return (T)value;
}
}
A small test class could then be:
public class Item
{
public string MyProperty { get; set; }
public string MyProperty3 { get; set; }
public string MyProperty2 { protected get; set; }
public Item()
{
MyProperty = "Test propery";
MyProperty3 = "Test property 3";
MyProperty2 = "Yoohoo";
}
}
With a main class for testing
class Program
{
static void Main(string[] args)
{
Item item = new Item();
for (int x = 0; x < 4; x++)
{
string key = "MyProperty" + (x > 0 ? x.ToString() : "");
string value = item.GetProperty<string>(key);
Console.WriteLine("Getting {0} = {1}", key, value);
}
Console.ReadLine();
}
}
which gives the expectable output of:
Getting MyProperty = Test propery
Getting MyProperty1 =
Getting MyProperty2 =
Getting MyProperty3 = Test property 3
I am working on some code that is written in C#. In this app, I have a custom collection defined as follows:
public class ResultList<T> : IEnumerable<T>
{
public List<T> Results { get; set; }
public decimal CenterLatitude { get; set; }
public decimal CenterLongitude { get; set; }
}
The type used by Results are one of three custom types. The properties of each of the custom types are just primitive types (ints, strings, bools, int?, bool?). Here is an example of one of the custom types:
public class ResultItem
{
public int ID { get; set; }
public string Name { get; set; }
public bool? isLegit { get; set; }
}
How do I perform a deep copy of a ResultList object that I've created. I found this post: Generic method to create deep copy of all elements in a collection. However, I can't figure out how to do it.
The approach involving the least coding effort is that of serializing and deserializing through a BinaryFormatter.
You could define the following extension method (taken from Kilhoffer’s answer):
public static T DeepClone<T>(T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T)formatter.Deserialize(ms);
}
}
…and then just call:
ResultList<T> clone = DeepClone(original);
One of the reasons why your ResultList class won't work with Jon Skeet's example is because it does not implement the ICloneable interface.
Implement ICloneable on all the classes that you need cloned, e.g.
public class ResultItem : ICloneable
{
public object Clone()
{
var item = new ResultItem
{
ID = ID,
Name = Name,
isLegit = isLegit
};
return item;
}
}
And also on ResultList:
public class ResultList<T> : IEnumerable<T>, ICloneable where T : ICloneable
{
public List<T> Results { get; set; }
public decimal CenterLatitude { get; set; }
public decimal CenterLongitude { get; set; }
public object Clone()
{
var list = new ResultList<T>
{
CenterLatitude = CenterLatitude,
CenterLongitude = CenterLongitude,
Results = Results.Select(x => x.Clone()).Cast<T>().ToList()
};
return list;
}
}
Then to make a deep copy of your object:
resultList.clone();
Expanding on #Georgi-it, I had to modify his code to handle properties whose type inherits List:
public static class ObjectCloner {
public static T Clone<T>(object obj, bool deep = false) where T : new() {
if (!(obj is T)) {
throw new Exception("Cloning object must match output type");
}
return (T)Clone(obj, deep);
}
public static object Clone(object obj, bool deep) {
if (obj == null) {
return null;
}
Type objType = obj.GetType();
if (objType.IsPrimitive || objType == typeof(string) || objType.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == 0) == null) {
return obj;
}
List<PropertyInfo> properties = objType.GetProperties().ToList();
if (deep) {
properties.AddRange(objType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic));
}
object newObj = Activator.CreateInstance(objType);
foreach (var prop in properties) {
if (prop.GetSetMethod() != null) {
var proceed = true;
if (obj is IList) {
var listType = obj.GetType().GetProperty("Item").PropertyType;
if (prop.PropertyType == listType) {
proceed = false;
foreach (var item in obj as IList) {
object clone = Clone(item, deep);
(newObj as IList).Add(clone);
}
}
}
if (proceed) {
object propValue = prop.GetValue(obj, null);
object clone = Clone(propValue, deep);
prop.SetValue(newObj, clone, null);
}
}
}
return newObj;
}
}
Here is something that I needed and wrote, it uses reflection to copy every property (and private ones if specified)
public static class ObjectCloner
{
public static T Clone<T>(object obj, bool deep = false) where T : new()
{
if (!(obj is T))
{
throw new Exception("Cloning object must match output type");
}
return (T)Clone(obj, deep);
}
public static object Clone(object obj, bool deep)
{
if (obj == null)
{
return null;
}
Type objType = obj.GetType();
if (objType.IsPrimitive || objType == typeof(string) || objType.GetConstructors().FirstOrDefault(x => x.GetParameters().Length == 0) == null)
{
return obj;
}
List<PropertyInfo> properties = objType.GetProperties().ToList();
if (deep)
{
properties.AddRange(objType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic));
}
object newObj = Activator.CreateInstance(objType);
foreach (var prop in properties)
{
if (prop.GetSetMethod() != null)
{
object propValue = prop.GetValue(obj, null);
object clone = Clone(propValue, deep);
prop.SetValue(newObj, clone, null);
}
}
return newObj;
}
}
For deep coping of an object you can use this code:
public static T DeepCopy<T>(T obj) {
var str = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
var ret = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(str);
return ret;
}
Does anybody know of a good algorithm to mutually exclusively check two properties using a ModelValidator?
Something like:
[EitherPropertyRequired("BuildingNumber","BuildingName"]
public class Address{
public int BuildingNumber { get; set; }
public string BuildingName { get; set; }
}
I ended up creating an attribute and manually checking it with a custom ModelValidator. This custom model validator is checked using an AssociatedValidatorProvider which is registered in Application_Start().
protected void Application_Start()
{
ModelValidatorProviders.Providers.Add(new ZipValidationProvider());
}
public class ZipValidationProvider:AssociatedValidatorProvider
{
protected override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
{
foreach (var attribute in attributes.OfType<EitherPropertyRequiredAttribute>())
{
yield return new EitherPropertyRequiredValidator(metadata,
context, attribute.FirstProperty, attribute.SecondProperty, attribute.Message);
}
}
}
[AttributeUsage(AttributeTargets.Class)]
public class EitherPropertyRequiredAttribute : Attribute
{
public readonly string FirstProperty;
public readonly string SecondProperty;
public readonly string Message;
public EitherPropertyRequiredAttribute(string firstProperty, string secondProperty,
string message)
{
FirstProperty = firstProperty;
SecondProperty = secondProperty;
Message = message;
}
}
public class EitherPropertyRequiredValidator:ModelValidator
{
private readonly string firstProperty;
private readonly string secondProperty;
private readonly string message;
public EitherPropertyRequiredValidator(ModelMetadata metadata,
ControllerContext context,
string firstProperty,
string secondProperty,
string message)
:base(metadata,context)
{
this.firstProperty = firstProperty;
this.secondProperty = secondProperty;
this.message = message;
}
private PropertyInfo GetPropertyInfoRecursive(Type type, string property)
{
var prop = type.GetProperty(property);
if (prop != null) return prop;
foreach (var p in type.GetProperties())
{
if (p.PropertyType.Assembly == typeof (object).Assembly)
continue;
return GetPropertyInfoRecursive(p.PropertyType, property);
}
return null;
}
private object GetPropertyValueRecursive(object obj, PropertyInfo propertyInfo)
{
Type objectType = obj.GetType();
if(objectType.GetProperty(propertyInfo.Name) != null)
return propertyInfo.GetValue(obj, null);
foreach (var p in objectType.GetProperties())
{
if (p.PropertyType.Assembly == typeof(object).Assembly)
continue;
var o = p.GetValue(obj,null);
return GetPropertyValueRecursive(o, propertyInfo);
}
return null;
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
if (Metadata.Model == null)
yield break;
var firstPropertyInfo = GetPropertyInfoRecursive(Metadata.Model.GetType(),firstProperty);
if(firstPropertyInfo == null)
throw new InvalidOperationException("Unknown property:" + firstProperty);
var secondPropertyInfo = GetPropertyInfoRecursive(Metadata.Model.GetType(),secondProperty);
if(secondPropertyInfo == null)
throw new InvalidOperationException("Unknown property:" + secondProperty);
var firstPropertyValue = GetPropertyValueRecursive(Metadata.Model, firstPropertyInfo);
var secondPropertyValue = GetPropertyValueRecursive(Metadata.Model, secondPropertyInfo);
bool firstPropertyIsEmpty = firstPropertyValue == null ||
firstPropertyValue.ToString().Length == 0;
bool secondPropertyIsEmpty = secondPropertyValue == null ||
secondPropertyValue.ToString().Length == 0;
if (firstPropertyIsEmpty && secondPropertyIsEmpty)
{
yield return new ModelValidationResult
{
MemberName = firstProperty,
Message = message
};
}
}
}
[AttributeUsage(AttributeTargets.Class)]
public class EitherPropertyRequiredAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// value will be the model
Address address = (Address)value;
// TODO: Check the properties of address here and return true or false
return true;
}
}
You could make this more generic by avoiding it casting to Address and using attribute properties and reflection.