I am trying to initialize all properties in class (lists) with using reflection:
public class EntitiesContainer
{
public IEnumerable<Address> Addresses { get; set; }
public IEnumerable<Person> People { get; set; }
public IEnumerable<Contract> Contracts { get; set; }
public EntitiesContainer()
{
var propertyInfo = this.GetType().GetProperties();
foreach (var property in propertyInfo)
{
property.SetValue(property, Activator.CreateInstance(property.GetType()), null);
}
}
}
I am getting exception:
No constructor has been defined for this object without parameters.
I would appreciate tips.
You can do this provided that you define the properties as concrete types. This actually works:
public class EntitiesContainer
{
public List<Address> Addresses { get; set; }
public List<Person> People { get; set; }
public List<Contract> Contracts { get; set; }
public EntitiesContainer()
{
var propertyInfo = this.GetType().GetProperties();
foreach (var property in propertyInfo)
{
property.SetValue(this, Activator.CreateInstance(property.PropertyType));
}
}
}
You cannot create an instance of an IEnumerable<T> because it's an interface.
But why would you want to to this? You'd better initialize the properties using the auto-property initializer that was introduced in C#6:
public class EntitiesContainer
{
public IEnumerable<Address> Addresses { get; set; } = new List<Address>;
public IEnumerable<Person> People { get; set; } = new List<Address>;
public IEnumerable<Contract> Contracts { get; set; } = new List<Address>;
}
In general here, the type of object you want to create is property.PropertyType; and the object upon which you want to set the value is this, so:
property.SetValue(this, Activator.CreateInstance(property.PropertyType), null);
But! your properties are IEnumerable<T>, not List<T> - can't create an interface, only a concrete type. So you'd have to do a lot of work with deconstructing the generic IEnumerable<Foo> to Foo (var args = type.GetGenericTypeArguments()) and constructing a List<Foo> (typeof(List<>).MakeGenericType(args)). Or just change the property types to List<T>!
Frankly, it would be easier to just do:
public IEnumerable<Address> Addresses { get; set; } = new List<Address>();
public IEnumerable<Person> People { get; set; } = new List<Person>();
public IEnumerable<Contract> Contracts { get; set; } = new List<Contract>();
or:
public List<Address> Addresses { get; } = new List<Address>();
public List<Person> People { get; } = new List<Person>();
public List<Contract> Contracts { get; } = new List<Contract>();
To sum up what I wanted to acheive was method called in constructor like below:
private void InitializeAllCollections()
{
var properties = this.GetType().GetProperties();
foreach (var property in properties)
{
var genericType = property.PropertyType.GetGenericArguments();
var creatingCollectionType = typeof(List<>).MakeGenericType(genericType);
property.SetValue(this, Activator.CreateInstance(creatingCollectionType));
}
}
Thanks guys for your help. :)
I had a similar need: when creating business objects for unit tests, I want to default all uninitialized Lists to new Lists, so that if a test needs to add something to a list, I don't have to worry about initializing it there. And like the OP, I have too many business objects to change them all to default. My solution is a mix of the others; the exceptions being I only want List properties, and only if they are not yet initialized:
public static T DefaultLists<T>(this T obj)
{
var properties = obj.GetType().GetProperties().Where(q => q.PropertyType.Name == "List`1" && q.GetValue(obj) == null);
foreach(var property in properties)
property.SetValue(obj, Activator.CreateInstance(property.PropertyType));
return obj;
}
Now my sample object creator can return new businessObject.DefaultLists();
Related
I have just started using AutoMapper on an asp net core project and I'm trying to map an object that has a collection of an object that also has a collection of an object to an entity.
The source
public class MyClass
{
public List<MyCollection> MyCollections { get; set; }
}
public class MyCollection
{
public int CollectionId { get; set; }
public List<Description> Descriptions { get; set; }
}
public class Description
{
public int DescriptionId { get; set; }
public string Text { get; set; }
}
The destination
public class DescriptionToCollection
{
public int DescriptionId { get; set; }
public int CollectionId { get; set; }
}
I've played around with ConvertUsing thinking something like this, but I can't make it work.
CreateMap<MyClass, List<DescriptionToCollection>>()
.ConvertUsing(source => source.MyCollections.Select(x =>x.Description.Select(y=> new DescriptionToCollection{ DescriptionId=y.DescriptionId,CollectionId=x.CollectionId}).ToList()
));
Searching AutoMappers docs and the internet, I couldn't find anything similar to my problem.
Any help is highly appreciated.
Besides a typo in your example code, you almost had it. Because you aren't mapping 1:1 at the top level, you need to flatten somewhere, and you do that using SelectMany, moving the ToList call appropriately.
CreateMap<MyClass, List<DescriptionToCollection>>()
.ConvertUsing(source => source.MyCollections.SelectMany(x => // SelectMany to flatten
x.Descriptions.Select(y =>
new DescriptionToCollection
{
DescriptionId = y.DescriptionId,
CollectionId = x.CollectionId
}
) // ToList used to be here
).ToList()
);
Try to implement ITypeConverter, follow the example code:
Your Classes
public class Class1
{
public List<Class2> class2 { get; set; }
}
public class Class2
{
public int CollectionId { get; set; }
public List<Class3> class3 { get; set; }
}
public class Class3
{
public int DescriptionId { get; set; }
public string Text { get; set; }
}
public class ClassDto
{
public int DescriptionId { get; set; }
public int CollectionId { get; set; }
}
The custom map
public class ClassCustomMap : ITypeConverter<Class1, List<ClassDto>>
{
public List<ClassDto> Convert(Class1 source, List<ClassDto> destination, ResolutionContext context)
{
var classDtoList = new List<ClassDto>();
foreach (var item in source.class2)
{
var collectionID = item.CollectionId;
foreach (var obj in item.class3)
{
var classDto = new ClassDto();
classDto.CollectionId = collectionID;
classDto.DescriptionId = obj.DescriptionId;
classDtoList.Add(classDto);
}
}
return classDtoList;
}
}
The mapping declaration
CreateMap<Class1, List<ClassDto>>().ConvertUsing<ClassCustomMap>();
How to use it
var class2 = new Class2();
class2.CollectionId = 2;
var class3 = new Class3();
class3.DescriptionId = 1;
class3.Text = "test";
class2.class3 = new System.Collections.Generic.List<Class3>() { class3 };
var class1 = new Class1();
class1.class2 = new System.Collections.Generic.List<Class2>() { class2 };
var result = mapper.Map<List<ClassDto>>(class1);
For clarity and to simplify I have used explicit cycles, if you want you can optimize the conversion function using LinQ and Lambda
You are missing the purpose of auto-mapper.
It should be used for transforming an input object of one type into an output object of a different type.
And you wanted to create a map from MyClass type to List - this should be treated as projection.
You can achieve that using LINQ (for example as a extension method on MyClass):
public static class MyClassExtension
{
public static List<DescriptionToCollection> ToDescriptionToCollection(this MyClass value)
{
return value.MyCollections.SelectMany(mc => mc.Descriptions.Select(d => new DescriptionToCollection()
{
CollectionId = mc.CollectionId,
DescriptionId = d.DescriptionId
})).ToList();
}
}
I am having trouble getting the property Names of the IEnumerable properties in my models. I cant seem to get the Nested IEnumerables from the TModel classes. I have looked into some reflection examples but haven't something quite along these lines.
I am looking to just get the IEnumerable property names for each nested model and send the property name to a list. The actual value is not important.
Any help would be greatly appreciated.
// TModel = DataContent in this context.
public class GetModelBase<TModel>
{
public string Error { get; set; }
public IEnumerable<TModel> DataContent { get; set; }
}
public class DataContent
{
public int Total { get; set; }
public IEnumerable<Data> Data { get; set; }
}
public class Data
{
public int DataId{ get; set; }
IEnumerable<DataInformation> DataInformation{ get; set; }
}
public IEnumerable<GetModelBase<TModel>> ResponseAsList<TModel>()
{
// ResponseBody in this context is a string representation of json of the models above...
var toArray = new ConvertJsonArray<GetModelBase<TModel>>(ResponseBody).ReturnJsonArray();
}
// T = GetModelBase<DataContent> in this context.
public class ConvertJsonArray<T>
{
public ConvertJsonArray(string responseString)
{
_responseString = responseString;
Convert();
}
public void Convert()
{
var result = JObject.Parse(_responseString);
// This is where I am having trouble... I am unable to get the nested IEnumerable names.
Type t = typeof(T);
PropertyInfo[] propertyInformation = t.GetProperties(BindingFlags.Public|BindingFlags.Instance);
List<string> toLists = new List<string>();
foreach (PropertyInfo pi in propertyInformation)
toLists.Add(pi.Name);
// End of Property Information Issuse...
foreach (string s in toLists.ToArray())
{
if (result[s] != null)
{
if (!(result[s] is JArray)) result[s] = new JArray(result[s]);
}
}
_jsonAsArray = result.ToString();
}
public string ReturnJsonArray()
{
return _jsonAsArray;
}
private string _responseString { get; set; }
private string _jsonAsArray { get; set; }
}
The result I am looking for in the above code sample would be a list containing only the IEnumerable names as such { "DataContent", "Data", "DataInformation" }
UPDATE:
I am still having trouble looping through each model. I have a nearly working code example.
// This replaces the Type code in the Convert method...
GetProperties(typeof(T))
private void GetProperties(Type classType)
{
foreach (PropertyInfo property in classType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (property.PropertyType.IsGenericType && (property.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
ValuesToList.Add(property.Name);
foreach (Type nestedType in property.PropertyType.GetGenericArguments())
{
GetProperties(nestedType);
}
}
}
}
private List<string> ValuesToList { get; set; }
The results for this yields { "DataContent", "Data" } but fails to get "DataInformation". For some reason the IEnumerables are not hit while in the foreach loop. Additional help would be appreciated.
You already have the PropertyInfo, so you are almost there - all that is left is to recognize which properties are of type IEnumerable<...>, where ... can be an arbitrary type.
For this purpose, check the PropertyType property.
It is a Type instance for which you can check whether it is based upon the generic type definition IEnumerable<T> by means of the GetGenericTypeDefinition method.
That method will throw an exception for non-generic types, so you will also have to check IsGenericType:
if (pi.PropertyType.IsGenericType
&& (pi.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
toLists.Add(pi.Name);
}
My question is, what is the best way I can map one object to another in the most maintainable manner. I cannot change the way the Dto object that we are getting is setup to be more normalized so I need to create a way to map this to our implementation of their object.
Here is example code to show what I need to happen:
class Program
{
static void Main(string[] args)
{
var dto = new Dto();
dto.Items = new object[] { 1.00m, true, "Three" };
dto.ItemsNames = new[] { "One", "Two", "Three" };
var model = GetModel(dto);
Console.WriteLine("One: {0}", model.One);
Console.WriteLine("Two: {0}", model.Two);
Console.WriteLine("Three: {0}", model.Three);
Console.ReadLine();
}
private static Model GetModel(Dto dto)
{
var result = new Model();
result.One = Convert.ToDecimal(dto.Items[Array.IndexOf(dto.ItemsNames, "One")]);
result.Two = Convert.ToBoolean(dto.Items[Array.IndexOf(dto.ItemsNames, "Two")]);
result.Three = dto.Items[Array.IndexOf(dto.ItemsNames, "Three")].ToString();
return result;
}
}
class Dto
{
public object[] Items { get; set; }
public string[] ItemsNames { get; set; }
}
class Model
{
public decimal One { get; set; }
public bool Two { get; set; }
public string Three { get; set; }
}
I think what would be great is if I had some sort of mapper class that would take in the model objects propertyInfo, the type I want to convert to, and the "itemname" I want to pull out. Does anyone have any suggestions to make this cleaner?
Thanks!
I would opt for AutoMapper, an open source and free mapping library which allows to map one type into another, based on conventions (i.e. map public properties with the same names and same/derived/convertible types, along with many other smart ones). Very easy to use, will let you achieve something like this:
Model model = Mapper.Map<Model>(dto);
Not sure about your specific requirements, but AutoMapper also supports custom value resolvers, which should help you writing a single, generic implementation of your particular mapper.
This is a possible generic implementation using a bit of reflection (pseudo-code, don't have VS now):
public class DtoMapper<DtoType>
{
Dictionary<string,PropertyInfo> properties;
public DtoMapper()
{
// Cache property infos
var t = typeof(DtoType);
properties = t.GetProperties().ToDictionary(p => p.Name, p => p);
}
public DtoType Map(Dto dto)
{
var instance = Activator.CreateInstance(typeOf(DtoType));
foreach(var p in properties)
{
p.SetProperty(
instance,
Convert.Type(
p.PropertyType,
dto.Items[Array.IndexOf(dto.ItemsNames, p.Name)]);
return instance;
}
}
Usage:
var mapper = new DtoMapper<Model>();
var modelInstance = mapper.Map(dto);
This will be slow when you create the mapper instance but much faster later.
Efran Cobisi's suggestion of using an Auto Mapper is a good one. I have used Auto Mapper for a while and it worked well, until I found the much faster alternative, Mapster.
Given a large list or IEnumerable, Mapster outperforms Auto Mapper. I found a benchmark somewhere that showed Mapster being 6 times as fast, but I could not find it again. You could look it up and then, if it is suits you, use Mapster.
/// <summary>
/// map properties
/// </summary>
/// <param name="sourceObj"></param>
/// <param name="targetObj"></param>
private void MapProp(object sourceObj, object targetObj)
{
Type T1 = sourceObj.GetType();
Type T2 = targetObj.GetType();
PropertyInfo[] sourceProprties = T1.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropertyInfo[] targetProprties = T2.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var sourceProp in sourceProprties)
{
object osourceVal = sourceProp.GetValue(sourceObj, null);
int entIndex = Array.IndexOf(targetProprties, sourceProp);
if (entIndex >= 0)
{
var targetProp = targetProprties[entIndex];
targetProp.SetValue(targetObj, osourceVal);
}
}
}
Using reflection
public interface IModelBase
{
int Id { get; set; }
}
public interface IDtoBase
{
int Id { get; set; }
}
public class Client : IModelBase
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<SomeType> ListOfSomeType { get; set; }
}
public class ClientDto : IDtoBase
{
public int Id { get; set; }
public string Name { get; set; }
}
public static class Extensions
{
public static TDto AsDto<T, TDto>(this T item)
where TDto : class, IDtoBase
where T : class, IModelBase
{
var list = item.GetType().GetProperties();
var inst = Activator.CreateInstance(typeof(TDto));
foreach (var i in list)
{
if (((TDto)inst).GetType().GetProperty(i.Name) == null)
continue;
var valor = i.GetValue(item, null);
((TDto)inst).GetType().GetProperty(i.Name).SetValue((TDto)inst, valor);
}
return (TDto)inst;
}
}
How to use it:
Client client = new { id = 1, Name = "Jay", ListOfSomeType = new List<SomeType>() };
ClientDto cdto = client.AsDto<Client, ClientDto>();
The fastest way to mapping two objects is inline-mapping, but maybe it took time so that you can use MappingGenerator
And also, you can see the benchmark from Jason Bock to compare, which is better below:
Full video on youtube
Note: I'm asking about subclasses, not derived classes.
Basically, what I need to do is check properties of an object and look for those that have a specific attribute set.
The problem I have is that a lot of the properties are from subclasses
public class ExampleAttribute : Attribute
{
public object Whatever { get; set; }
}
public class MiddleEarth
{
[Example]
public Type EntityType { get; set; }
}
public class Elf : MiddleEarth
{
[Example]
public SubClass ItsLateAndImTired { get; set; }
public IList<Arg> Args { get; set; }
//Need to check properties of this object as well
public class SubClass
{
public object SubProperty { get; set; }
[Example]
public object SubPropertyWithAttribute { get; set; }
}
public class Arg
{
[Example]
public string Something { get; set; }
}
}
Now, I'm trying to do it as follows...but for reasons noted in the comments it won't work
public List<string> IterateProperties(object _o)
{
List<string> problems = new List<string>();
foreach (PropertyInfo info in _o.GetType().GetProperties())
{
//All 3 of these will return the exact same thing
Type thisType = this.GetType();
Type oType = _o.GetType();
Type infoType = info.ReflectedType;
//IsSubClassOf only checks for derived classes,
//so it's not the method I'm looking for
if (info.ReflectedType.IsSubclassOf(this.GetType()))
{
object sub = info.GetValue(_o, null);
if (sub != null)
{
problems.AddRange(this.IterateProperties(sub));
}
}
object[] attributes = info.GetCustomAttributes(typeof(ExampleAttribute), true);
foreach (object o in attributes)
{
if (info.GetValue(_o, null) == null)
{
problems.Add(String.Format("Attribute {0} in class {1} cannot be null", info.Name, info.ReflectedType.ToString()));
}
}
}
return problems;
}
Any ideas?
I believe what you're looking for is Type.GetNestedTypes()
http://msdn.microsoft.com/en-GB/library/493t6h7t.aspx
I'm not sure, but think that GetProperties method got some flags that can help...
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-null values from an instance of Defect into an instance of DefectViewModel. I was hoping to do it 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? Should I maintain separate lists of Defect properties and DefectViewModel properties so that I can do viewModelProperty.SetValue(viewModel, defectValue, null)?
Edit: thanks to both Jordão's and Dave's answers, 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 that PropertyInfo business, I just have the following line:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
Mapper.Map<Defect, DefectViewModel>(defect, defectViewModel);
Take a look at AutoMapper.
There are frameworks for this, the one I know of is Automapper:
http://automapper.codeplex.com/
http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/01/22/automapper-the-object-object-mapper.aspx
Replace your erroneous line with this:
PropertyInfo targetProperty = defectViewModel.GetType().GetProperty(defectProperty.Name);
targetProperty.SetValue(viewModel, defectValue, null);
Your posted code is attempting to set a Defect-tied property on a DefectViewModel object.
In terms of organizing the code, if you don't want an external library like AutoMapper, you can use a mixin-like scheme to separate the code out like this:
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)));
}
}
This way, all the code to achieve the property-copying is separate from the classes that use it. You just need to reference the mixins in their interface list.
Note that this is not as robust or flexible as AutoMapper, because you might want to copy properties with different names or just some sub-set of the properties. Or it might downright fail if the properties don't provide the necessary getters or setters or their types differ. But, it still might be enough for your purposes.
This is cheap and easy. It makes use of System.Web.Script.Serialization and some extention 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's some similiar 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's 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
For one thing I would not place that code (somewhere) external but in the constructor of the ViewModel:
class DefectViewModel
{
public DefectViewModel(Defect source) { ... }
}
And if this is the only class (or one of a few) I would not automate it further but write out the property assignments. Automating it looks nice but there may be more exceptions and special cases than you expect.
Any chance you could have both classes implement an interface that defines the shared properties?