C # - copying property values ​​from one instance to another of different classes

I have two C # classes that have many of the same properties (by name and type). I want to be able to copy all non-zero values ​​from a Defect instance to a DefectViewModel instance. I was hoping to do this with reflection using GetType().GetProperties() . I tried the following:

 var defect = new Defect(); var defectViewModel = new DefectViewModel(); PropertyInfo[] defectProperties = defect.GetType().GetProperties(); IEnumerable<string> viewModelPropertyNames = defectViewModel.GetType().GetProperties().Select(property => property.Name); IEnumerable<PropertyInfo> propertiesToCopy = defectProperties.Where(defectProperty => viewModelPropertyNames.Contains(defectProperty.Name) ); foreach (PropertyInfo defectProperty in propertiesToCopy) { var defectValue = defectProperty.GetValue(defect, null) as string; if (null == defectValue) { continue; } // "System.Reflection.TargetException: Object does not match target type": defectProperty.SetValue(viewModel, defectValue, null); } 

What would be the best way to do this? Do I have to maintain separate Defect and DefectViewModel property lists so that I can do viewModelProperty.SetValue(viewModel, defectValue, null) ?

Edit: Thanks to the answers of Jordão and Dave , I chose AutoMapper. DefectViewModel is in a WPF application, so I added the following App constructor:

 public App() { Mapper.CreateMap<Defect, DefectViewModel>() .ForMember("PropertyOnlyInViewModel", options => options.Ignore()) .ForMember("AnotherPropertyOnlyInViewModel", options => options.Ignore()) .ForAllMembers(memberConfigExpr => memberConfigExpr.Condition(resContext => resContext.SourceType.Equals(typeof(string)) && !resContext.IsSourceValueNull ) ); } 

Then, instead of all this PropertyInfo business, I only have the following line:

 var defect = new Defect(); var defectViewModel = new DefectViewModel(); Mapper.Map<Defect, DefectViewModel>(defect, defectViewModel); 
+4
reflection c # properties mapping
Aug 31 '10 at 16:03
source share
7 answers

Take a look at AutoMapper .

+8
Aug 31 '10 at 16:06
source share
+3
Aug 31 '10 at 16:07
source share

Replace your error string as follows:

 PropertyInfo targetProperty = defectViewModel.GetType().GetProperty(defectProperty.Name); targetProperty.SetValue(viewModel, defectValue, null); 

Your published code is trying to set the Defect -tied property to a DefectViewModel .

+2
Aug 31 '10 at 16:16
source share

In terms of code organization, if you do not want to use an external library such as AutoMapper, you can use the mixin-like scheme to separate the code as follows:

 class Program { static void Main(string[] args) { var d = new Defect() { Category = "bug", Status = "open" }; var m = new DefectViewModel(); m.CopyPropertiesFrom(d); Console.WriteLine("{0}, {1}", m.Category, m.Status); } } // compositions class Defect : MPropertyGettable { public string Category { get; set; } public string Status { get; set; } // ... } class DefectViewModel : MPropertySettable { public string Category { get; set; } public string Status { get; set; } // ... } // quasi-mixins public interface MPropertyEnumerable { } public static class PropertyEnumerable { public static IEnumerable<string> GetProperties(this MPropertyEnumerable self) { return self.GetType().GetProperties().Select(property => property.Name); } } public interface MPropertyGettable : MPropertyEnumerable { } public static class PropertyGettable { public static object GetValue(this MPropertyGettable self, string name) { return self.GetType().GetProperty(name).GetValue(self, null); } } public interface MPropertySettable : MPropertyEnumerable { } public static class PropertySettable { public static void SetValue<T>(this MPropertySettable self, string name, T value) { self.GetType().GetProperty(name).SetValue(self, value, null); } public static void CopyPropertiesFrom(this MPropertySettable self, MPropertyGettable other) { self.GetProperties().Intersect(other.GetProperties()).ToList().ForEach( property => self.SetValue(property, other.GetValue(property))); } } 

Thus, all code to achieve copying properties is separated from classes that use it. You just need to reference mixins in their list of interfaces.

Note that this is not as reliable or flexible as AutoMapper, because you can copy properties with different names or with only a subset of properties. Or it may fail if the properties do not provide the necessary getters or setters or their types. But this may still be enough for your purposes.

+2
Aug 31 '10 at 16:59
source share

It is cheap and easy. It uses System.Web.Script.Serialization and some extension methods for ease of use:

 public static class JSONExts { public static string ToJSON(this object o) { var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); return oSerializer.Serialize(o); } public static List<T> FromJSONToListOf<T>(this string jsonString) { var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); return oSerializer.Deserialize<List<T>>(jsonString); } public static T FromJSONTo<T>(this string jsonString) { var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); return oSerializer.Deserialize<T>(jsonString); } public static T1 ConvertViaJSON<T1>(this object o) { return o.ToJSON().FromJSONTo<T1>(); } } 

Here are some similar but different classes:

 public class Member { public string Name { get; set; } public int Age { get; set; } public bool IsCitizen { get; set; } public DateTime? Birthday { get; set; } public string PetName { get; set; } public int PetAge { get; set; } public bool IsUgly { get; set; } } public class MemberV2 { public string Name { get; set; } public int Age { get; set; } public bool IsCitizen { get; set; } public DateTime? Birthday { get; set; } public string ChildName { get; set; } public int ChildAge { get; set; } public bool IsCute { get; set; } } 

And here are the methods in action:

 var memberClass1Obj = new Member { Name = "Steve Smith", Age = 25, IsCitizen = true, Birthday = DateTime.Now.AddYears(-30), PetName = "Rosco", PetAge = 4, IsUgly = true, }; string br = "<br /><br />"; Response.Write(memberClass1Obj.ToJSON() + br); // just to show the JSON var memberClass2Obj = memberClass1Obj.ConvertViaJSON<MemberV2>(); Response.Write(memberClass2Obj.ToJSON()); // valid fields are filled 
+2
Aug 21 2018-12-21T00:
source share

On the one hand, I would not put this code (somewhere) external, but in the ViewModel constructor:

 class DefectViewModel { public DefectViewModel(Defect source) { ... } } 

And if this is the only class (or one of several), I would not automate it further, but write out the assignment of properties. Automation looks good, but there may be more exceptions and special cases than you expect.

+1
Aug 31 '10 at 16:20
source share

Is it likely that both classes implement an interface that defines common properties?

0
Aug 31 '10 at 16:06
source share



All Articles