I am using this method to copy properties of one object to another and its working fine.
But today i found that its not working for arrays of different object.
please help me in this.
public static class CopyClass
{
/// <summary>
/// Copies source object properties to target object properties.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="target">The target.</param>
public static void CopyTo(object source, object target)
{
foreach (PropertyInfo propSource in source.GetType().GetProperties())
{
foreach (PropertyInfo propTarget in target.GetType().GetProperties())
{
if (propTarget.Name != propSource.Name) continue;
(propTarget.GetSetMethod()).Invoke(target,
new object[] { propSource.GetGetMethod().Invoke(source, null) });
}
}
}
}
Can you check with this function? I am not sure if this is working with Arrays but it sure does with plain members.
public static object ObjectCopyProperties(this object sourceObject, object targetObject)
{
if (sourceObject == null || targetObject == null)
return null;
var targetInstance = targetObject;
PropertyInfo newProp;
foreach (PropertyInfo prop in sourceObject.GetType().GetProperties())
{
if (prop.CanRead)
{
newProp = targetInstance.GetType().GetProperty(prop.Name);
if (newProp != null && newProp.CanWrite)
{
newProp.SetValue(targetInstance, prop.GetValue(sourceObject, null), null);
}
}
}
return targetInstance;
}
Given the following 2 classes
public class UserType1
{
public DateTime Created { get; set; }
public string First { get; set; }
public Gender Genter { get; set; }
public int Id { get; set; }
public string Last { get; set; }
public DateTime Updated { get; set; }
public string DontMatchType { get; set; }
public string Unique1 { get; set; }
}
public class UserType2
{
public DateTime Created { get; set; }
public string First { get; set; }
public Gender Genter { get; set; }
public int Id { get; set; }
public string Last { get; set; }
public DateTime Updated { get; set; }
public int DontMatchType { get; set; }
public string Unique2 { get; set; }
}
and the following code
UserType1 user1 = new UserType1
{
Id = 1,
First = "John",
Last = "Doe",
Genter = Gender.Male,
Created = DateTime.Now.AddDays(-1),
Updated = DateTime.Now,
DontMatchType = "won't map",
Unique1 = "foobar"
};
UserType2 user2 = CopyTo<UserType2>(user1);
you can see that this mapping function will only map matching name/types
public static T CopyTo<T>(object source)
where T : new()
{
if (source == null) throw new ArgumentException("surce is null", "source");
T target = new T();
source.GetType()
.GetProperties()
.Join(target.GetType().GetProperties()
, s => s.Name
, t => t.Name
, (s, t) => new
{
source = s,
target = t
})
.AsParallel()
.Where(inCommon => inCommon.source.PropertyType == inCommon.target.PropertyType
&& inCommon.source.CanRead && inCommon.target.CanWrite)
.ForAll(inCommon => inCommon.target.SetValue(target, inCommon.source.GetValue(source, null), null));
return target;
}
and you can use
public static IEnumerable<T> CopyTo<T>(IEnumerable<object> source)
where T : new()
{
return source.AsParallel().Select(CopyTo<T>);
}
to copy a collection like this
UserType1[] users1 = new[]
{
new UserType1
{
...
}
};
UserType2[] users2 = CopyTo<UserType2>(users1).ToArray();
This has the added benefit that it will not needlessly loop all the properties of object B for every property in object A, as its using a join to find the properties in common (by name and type).
Mapping can be tricky. You can get into a lot of situations of name/types and nesting. I would suggest Automapper as jjchiw mentioned.
If your object is serializable you can create an extension method which serialize the source object and deserialize it in return
public static class CloneExtensions
{
public static T Clone<T>(this T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
if(source == default(T))
return default(T);
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
}
Related
I'd like an extension method to create an object based on another but keep only the primitive properties. This object will be dumped into a log file in JSON format for logging.
Based on the classes shown below, in this sample, the created object should keep only these properties :
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
I am using .NET Framework 4.7
How can I do this?
// To use like this
var order = new Order();
var forLog = order.RemovePrimitives();
// Sample of classes
public class Order
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public List<Item> Items { get; set; }
public Address Address { get; set; }
}
public class Item{}
public class Address{}
public static class Extensions
{
public static string RemovePrimitives(this object obj)
{
// I need to create an anonymous, named 'TheNewObjectHere' object but only with primitives
// I will dump the object to push to a log file. I need only primitives
return JsonConvert.SerializeObject(TheNewObjectHere, Formatting.Indented);
}
}
Thanks
try this
public static class Extensions
{
public static string RemovePrimitives(this object obj)
{
var jsonObj = JObject.FromObject(obj);
var propToRemove = jsonObj.Properties().Where(i => !i.Value.GetType().ToString()
.Contains("JValue")).ToList();
foreach (var prop in propToRemove) prop.Remove();
return jsonObj.ToString();
}
}
You can use reflection to get primitive properties and then use JObject to build a JSON object dynamically.
public static readonly Type[] AdditionalPrimities = new[] { typeof(decimal), typeof(string) };
public static string RemovePrimitives<T>(this T obj)
{
var jObj = new JObject();
var props = GetPrimitiveProperties(obj);
foreach (var item in props)
{
var value = item.GetValue(obj);
if (value != null)
{
jObj.Add(item.Name, new JValue(value));
}
}
return jObj.ToString(Newtonsoft.Json.Formatting.Indented);
}
public static PropertyInfo[] GetPrimitiveProperties<T>()
{
var properties = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(r => r.PropertyType.IsPrimitive || (r.PropertyType.IsGenericType && Nullable.GetUnderlyingType(r.PropertyType) != null) || AdditionalPrimities.Contains(r.PropertyType))
.Select(r => r)
.ToArray();
return properties;
}
public static void Main()
{
var order = new Order { FirstName = "abc", LastName = "cde", Address = new Address(), Age2 = 3, Age = 1 };
var final = order.RemovePrimitives();
Console.WriteLine(final);
}
Fiddle
This question already has answers here:
Recursively Get Properties & Child Properties Of A Class
(5 answers)
Closed 2 years ago.
I am trying to write an universal search to use for all objects.
I have this code, which is working fine to search in just one object's properties, but I would also like to search also in properties in related objects.
Eg. I have these Models/Objects
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address{ get; set; }
public ICollection<Contract> Contracts { get; set; }
}
public class Contract
{
public int Id { get; set; }
public DateTime From{ get; set; }
public DateTime To{ get; set; }
public string Comment{ get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
}
and I want to search if any of properties contains some a string eg. "Peter", I will call it this way:
string searchString = "Peter";
var customers = db.Customers
.Include(x => x.Contracts)
.WhereAnyPropertiesOfSimilarTypeContains(searchString);
this code will check if any properties of 'Customer' contains string "Peter".
But I would also need to check if the related model 'Contract' contains "Peter.
public static class EntityHelper
{
public static IQueryable<TEntity> WhereAnyPropertiesOfSimilarTypeContains<TEntity, TProperty>(this IQueryable<TEntity> query, TProperty value)
{
var param = Expression.Parameter(typeof(TEntity));
var predicate = PredicateBuilder.False<TEntity>(); //--- True to equal
var entityFields = GetEntityFieldsToCompareTo<TEntity, TProperty>();
foreach (var fieldName in entityFields)
{
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var predicateToAdd = Expression.Lambda<Func<TEntity, bool>>(
Expression.Call(
Expression.PropertyOrField(param, fieldName), method,
Expression.Constant(value)), param);
predicate = predicate.Or(predicateToAdd); //--- And to equal
}
return query.Where(predicate);
}
// TODO: You'll need to find out what fields are actually ones you would want to compare on.
// This might involve stripping out properties marked with [NotMapped] attributes, for
// for example.
public static IEnumerable<string> GetEntityFieldsToCompareTo<TEntity, TProperty>()
{
Type entityType = typeof(TEntity);
Type propertyType = typeof(TProperty);
var fields = entityType.GetFields()
.Where(f => f.FieldType == propertyType)
.Select(f => f.Name);
var properties = entityType.GetProperties()
.Where(p => p.PropertyType == propertyType)
.Select(p => p.Name);
return fields.Concat(properties);
}
}
Thanks.
After reread the question. I don't know what are you trying, but here I put the idea I have what are you looking for.
public class Customer : AbstractEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public ICollection<Contract> Contracts { get; set; }
}
public class Contract : AbstractEntity
{
//what property here can be string "Peter"? Comments?
//what are you trying?
public int Id { get; set; }
public DateTime From { get; set; }
public DateTime To { get; set; }
public string Comment { get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
}
public abstract class AbstractEntity
{
//this method can be used to preselect properties you want
protected virtual Tuple<bool, ICollection<PropertyInfo>> PropertyCollector()
{
return new Tuple<bool, ICollection<PropertyInfo>>(false, null);
}
public IEnumerable<Tuple<Type, object>> GetRowValues()
{
foreach (var prop in GetRows())
{
yield return new Tuple<Type, object>(prop.PropertyType, prop.GetValue(this));
}
}
public ICollection<PropertyInfo> GetRows()
{
var tuple = PropertyCollector();
ISet<PropertyInfo> pInfo;
if (tuple.Item1)
{
pInfo = new HashSet<PropertyInfo>(tuple.Item2);
}
else //search all non virtual, private, protected properties, "following POCO scheme"
{
pInfo = new HashSet<PropertyInfo>();
foreach (var prop in GetType().GetProperties())
{
foreach (var access in prop.GetAccessors())
{
if ((!access.IsVirtual && !access.IsPrivate) && (prop.CanWrite && prop.CanRead))
{
pInfo.Add(prop);
}
}
}
}
return pInfo;
}
}
public static class Searchs
{
public static ICollection<object> ObjectsWithStringFound(ICollection<Customer> customers, string toBeFound)
{
var objs = new List<object>();
foreach (var cust in customers)
{
var strings = cust.GetRowValues().Where(tpl => tpl.Item1 == typeof(string)).Select(tpl => tpl.Item2);
var contracts = cust.GetRowValues().Where(tpl => tpl.Item2 is IEnumerable<Contract>).Select(tpl => tpl.Item2);
if (strings.Any(str => str == toBeFound))
{
objs.Add(cust);
}
else if (contracts.Any(ctr => ((IEnumerable<Contract>)ctr).!!!!!!!!! == toBeFound))
{ //What I suppose I must "match" with "Peter"??!?!
objs.Add(contracts.First(ctr => ((IEnumerable<Contract>)ctr).!!!!!!!!! == toBeFound));
}
}
return objs;
}
}
I think we aren't understanding each other.
what I am trying to find away to move DynamicProperties property to JSONdata property because of that I have this reflection function to do the job when it came to DynamicProperties it throws exception "System.InvalidCastException: 'Object must implement IConvertible.'" can someone help me?
public IHttpActionResult Get(ODataQueryOptions<Client> options)
{
if(queryNew.ElementType == typeof(Client)){}
else //if (queryNew.ElementType.Name == "SelectSome`1")
{
var results = new List<Client>();
try
{
foreach (var item in queryNew)
{
var dict = ((ISelectExpandWrapper)item).ToDictionary();
var model = DictionaryToObject<Client>(dict);
results.Add(model);
}
return Ok(results);
}
catch (Exception)
{
throw;
}
}
private static T DictionaryToObject<T>(IDictionary<string, object> dict) where T : new()
{
T t = new T();
PropertyInfo[] properties = t.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
if (!dict.Any(x => x.Key.Equals(property.Name, StringComparison.InvariantCultureIgnoreCase)))
continue;
KeyValuePair<string, object> item = dict.First(x => x.Key.Equals(property.Name, StringComparison.InvariantCultureIgnoreCase));
Type tPropertyType = t.GetType().GetProperty(property.Name).PropertyType;
Type newT = Nullable.GetUnderlyingType(tPropertyType) ?? tPropertyType;
if(dict.Any(x => x.Key.Equals("DynamicProperties", StringComparison.InvariantCultureIgnoreCase)))
{
object temp = JsonConvert.SerializeObject(item.Value, Formatting.Indented); //Convert.ChangeType(item.Value.ToString(), newT);
t.GetType().GetProperty("JsonData").SetValue(t, temp, null);
}
object newA = Convert.ChangeType(item.Value, newT);
t.GetType().GetProperty(property.Name).SetValue(t, newA, null);
}
return t;
}
client class
public class Client
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string ParticipantId { get; set; }
public DateTime? BirthDate { get; set; }
public int Gender { get; set; }
public byte? Age { get; set; }
public int Status { get; set; }
public string Notes { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime? UpdatedDate { get; set; }
public DateTime? DeletedDate { get; set; }
public bool IsDeleted { get; set; }
public int? DefaultLanguageId { get; set; }
public Guid UserId { get; set; }
public string JsonData { get; set; }
public virtual ICollection<ClientTag> ClientTags { get; private set; }
protected IDictionary<string, object> _dynamicProperties;
public IDictionary<string, object> DynamicProperties
{
get
{
if (_dynamicProperties == null)
{
if (this.JsonData == null)
{
_dynamicProperties = new Dictionary<string, object>();
}
else
{
_dynamicProperties = Mapper.Map<IDictionary<string, object>>(JObject.Parse(this.JsonData));
}
//_dynamicProperties.Source.ListChanged += (sender, e) => { this.AssessmentData = _dynamicProperties.Source.ToString(); };
}
return _dynamicProperties;
}
}
public void UpdateJsonDataFromDynamicProperties()
{
this.JsonData = Mapper.Map<JObject>(_dynamicProperties).ToString();
}
}
When you get an IConvertable error, that means that you have tried to assign one type to another.
for example, if you try to assign textbox to string, you will get an error because you cannot assign object to string, you will have to use Iconvertible.
e.g.:
String.ToString();
Which implicitly implements Iconvertible.
I cannot tell exactly where your code fails, you didn't mention it either, I can just tell you that somewhere in the client methods, you are trying to assign two different types and you just cannot do that.
Use debug on both the server and client class and check where you are trying to assign two different types, then implement the desired Iconvertible, meaning make the needed conversion.
The only problem in your code here, is that you are trying to assign to different types.
My guess, is that this line caused trouble:
if (this.JsonData == null)
{
_dynamicProperties = new Dictionary<string, object>();
}
else
{
_dynamicProperties = Mapper.Map<IDictionary<string, object>>(JObject.Parse(this.JsonData));
}
When you are trying to assign _dynamicProperties into a dictionary object when in fact you declared _dynamicProperties as IDictionary object.
There is a subtle different between Idictionary and dictionary object, they are not of the same type. this is the change need to be done:
if (this.JsonData == null)
{
_dynamicProperties = new IDictionary<string, object>();
}
else
{
_dynamicProperties = Mapper.Map<IDictionary<string, object>>(JObject.Parse(this.JsonData));
}
I have three models as describe below:
public class ComponentData //Contains list of Component Model
{
public int id { get; set; }
public List<Component> Components { get; set; }
}
public class Component //Contains list of ComponentValue Model
{
public Int32 SiteId { get; set; }
public IList<ComponentValue> ComponentValues { get; set; }
}
public class ComponentValue //Contains list of it self i.e. ComponentValue Model
{
public String Id { get; set; }
public String Name { get; set; }
public String DisplayName { get; set; }
public IList<ComponentValue> ChildComponents { get; set; }
}
Now I have two objects old ComponentData and new ComponentData, I want to compare these two object and want to find whether new list has any new child added or any changes in existing child. How to do that?
Note : public IList<ComponentValue> ChildComponents { get; set; } can have multiple children in it recursively.
I tried:
using (var e1 = cdOld.Components.GetEnumerator())
using (var e2 = cdNew.Components.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext())
{
var item1 = e1.Current.ComponentValues;
var item2 = e2.Current.ComponentValues;
using (var i1 = item1.GetEnumerator())
using (var i2 = item2.GetEnumerator())
{
while (i1.MoveNext() && i2.MoveNext())
{
//Here not sure how many children both list has and how to make recursive call
var item11 = i1.Current;
var item12 = i2.Current;
if (item11.Id != item12.Id || item11.Name != item12.Name)
{
cvlistold.Add(item11);
cvlistnew.Add(item12);
}
}
}
//var firstNotSecond = item1.Except(item2).ToList();
//var secondNotFirst = item2.Except(item1).ToList();
//var v = item1.Select(a => a.Name).Intersect(item2.Select(b => b.Name)).ToList();
//bool c = !(!firstNotSecond.Any() && !secondNotFirst.Any());
}
}
Screenshot:
Use IEquatable interface (or override Equals and GetHashCode):
public class ComponentValue : IEquatable<ComponentValue>
{
public String Id { get; set; }
public String Name { get; set; }
public String DisplayName { get; set; }
public IList<ComponentValue> ChildComponents { get; set; }
public bool Equals(ComponentValue other)
{
return Id == other.Id && Name == other.Name;
}
}
Then, check the differences:
bool equals = e1.SequenceEqual(e2);
I had similar structure in one of my projects with the difference that they could've contain more element types then one so I've made some reflection code to look up and compare these :
public static bool Compare ( this T obj, T comparer )
{
bool isOkay = true;
foreach(var field in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
{
if (!isOkay) break;
object value = field.GetValue(obj);
object comparerValue = field.GetValue(comparer);
Type type = field.FieldType;
if(Type.GetTypeCode(type) != TypeCode.Object)
{
if(type == typeof(IList))
{
for(int i = 0; i < ((IList)value).Count; i++)
{
isOkay = isOkay &&(bool)_GenericCompare.MakeGenericMethod(((IList)value)[i].GetType()).Invoke(((IList)value)[i], ((IList)comparerValue)[i]);
}
}
}
else
{
isOkay = isOkay && value.Equals(comparerValue);
}
}
return isOkay;
}
// _GenericVompare is :
typeof(MeTypeThatHasCompareMethod).GetMethod("Compare", BindingFlags.Static | BindingFlags.Public);
This is just a shortened method which I've done so you should modify this for your needs but basically it iterates through all items of IList typed field and Invokes the same method on that items.
FYI. This method can be slow as hell on huge lists so you should implement some kind of "cache" mechanism to store FieldInfo[] for some types and _GenericCompare MethodInfo to speed up the process.
,I have one class in which I have three properties now what I want to do, if in the object if any one of null or empty then I want to remove it from the object below is my code.
public class TestClass
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
TestClass t=new TestClass();
t.Address="address";
t.ID=132;
t.Name=string.Empty;
t.DateTime=null;
Now here I want the object of TestClass but in that Name and DateTime property should not be their in the object,
is it possible?
Please help me
There's no such concept as removing a property from an individual object. The type decided which properties are present - not individual objects.
In particular, it will always be valid to have a method like this:
public void ShowDateTime(TestClass t)
{
Console.WriteLine(t.DateTme);
}
That code has no way of knowing whether you've wanted to "remove" the DateTime property from the object that t refers to. If the value is null, it will just get that value - that's fine. But you can't remove the property itself.
If you're listing the properties of an object somewhere, you should do the filtering there, instead.
EDIT: Okay, no you've given us some context:
ok I am using Schemaless database so null and empty value also store space in database that's the reason
So in the code you're using which populates that database, just don't set any fields which corresponds to properties with a null value. That's purely a database population concern - not a matter for the object itself.
(I'd also argue that you should consider how much space you'll really save by doing this. Do you really care that much?)
I was bored and got this in LINQPad
void Main()
{
TestClass t=new TestClass();
t.Address="address";
t.ID=132;
t.Name=string.Empty;
t.DateTime=null;
t.Dump();
var ret = t.FixMeUp();
((object)ret).Dump();
}
public static class ReClasser
{
public static dynamic FixMeUp<T>(this T fixMe)
{
var t = fixMe.GetType();
var returnClass = new ExpandoObject() as IDictionary<string, object>;
foreach(var pr in t.GetProperties())
{
var val = pr.GetValue(fixMe);
if(val is string && string.IsNullOrWhiteSpace(val.ToString()))
{
}
else if(val == null)
{
}
else
{
returnClass.Add(pr.Name, val);
}
}
return returnClass;
}
}
public class TestClass
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
Hereby a 'slightly' more clear and shorter version of the accepted answer.
/// <returns>A dynamic object with only the filled properties of an object</returns>
public static object ConvertToObjectWithoutPropertiesWithNullValues<T>(this T objectToTransform)
{
var type = objectToTransform.GetType();
var returnClass = new ExpandoObject() as IDictionary<string, object>;
foreach (var propertyInfo in type.GetProperties())
{
var value = propertyInfo.GetValue(objectToTransform);
var valueIsNotAString = !(value is string && !string.IsNullOrWhiteSpace(value.ToString()));
if (valueIsNotAString && value != null)
{
returnClass.Add(propertyInfo.Name, value);
}
}
return returnClass;
}
You could take advantage of the dynamic type:
class Program
{
static void Main(string[] args)
{
List<dynamic> list = new List<dynamic>();
dynamic
t1 = new ExpandoObject(),
t2 = new ExpandoObject();
t1.Address = "address1";
t1.ID = 132;
t2.Address = "address2";
t2.ID = 133;
t2.Name = "someName";
t2.DateTime = DateTime.Now;
list.AddRange(new[] { t1, t2 });
// later in your code
list.Select((obj, index) =>
new { index, obj }).ToList().ForEach(item =>
{
Console.WriteLine("Object #{0}", item.index);
((IDictionary<string, object>)item.obj).ToList()
.ForEach(i =>
{
Console.WriteLine("Property: {0} Value: {1}",
i.Key, i.Value);
});
Console.WriteLine();
});
// or maybe generate JSON
var s = JsonSerializer.Create();
var sb=new StringBuilder();
var w=new StringWriter(sb);
var items = list.Select(item =>
{
sb.Clear();
s.Serialize(w, item);
return sb.ToString();
});
items.ToList().ForEach(json =>
{
Console.WriteLine(json);
});
}
}
May be interfaces will be handy:
public interface IAdressAndId
{
int ID { get; set; }
string Address { get; set; }
}
public interface INameAndDate
{
string Name { get; set; }
DateTime? DateTime { get; set; }
}
public class TestClass : IAdressAndId, INameAndDate
{
public string Name { get; set; }
public int ID { get; set; }
public DateTime? DateTime { get; set; }
public string Address { get; set; }
}
Creating object:
IAdressAndId t = new TestClass()
{
Address = "address",
ID = 132,
Name = string.Empty,
DateTime = null
};
Also u can put your interfaces in separate namespace and make your class declaration as internal. After that create some public factories which will create the instances of your classes.