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
Related
I have an API providing tons of functionality, thatswhy there are massive abstractions to generics.
Please imagine (with the code below) following scenario: Every Dto has an Id. I want to test against a Service, create the items. The service doenst know the Id, because it gets generated by the DbContext.
So far so regualar.
But now imagine, i have a nested Dto, like in the example ComplexDto contains a SampleDto. So my TestHelper should test all the IDto, and ignore ALL Ids (the one of ComplexDto AND SampleDto), just by having the Interface.
DTO-Setup:
public interface IDto
{
long Id { get; }
}
public abstract record BaseDto : IDto
{
public long Id { get; init; }
}
public sealed record SampleDto : BaseDto
{
public string FooString { get; init; } = string.Empty;
}
public sealed record ComplexDto : BaseDto
{
public string AnotherFooString { get; init; } = string.Empty;
public SampleDto SampleDto { get; init; } = default!;
}
Test-Setup
[Test]
public void TestDummy()
{
var dto = new ComplexDto()
{
Id = 1, // for demo porposes
SampleDto = new SampleDto
{
Id = 1, // for demo porposes
FooString = "hi"
}
};
var testHelper = new TestHelper<ComplexDto>();
testHelper.CheckStuff(dto);
}
public class TestHelper<IDto>
{
public void CheckStuff(IDto dto)
{
var anotherDto = new ComplexDto()
{
Id = 123, // for demo porposes
SampleDto = new SampleDto
{
Id = 123, // for demo porposes
FooString = "hi"
}
};
//fails
//ComplexDto.Id gets ignored
//SampleDto.Id does not get ignored!!!
anotherDto.Should().BeEquivalentTo(dto, options => options.Excluding(x => x.Id));
}
}
So actually, what i am looking for, is in FluentAssertions-Api something like ExcludeAll("Id") or ExcludeRecursive("Id"). I could help myself with some ugly reflection, but maybe there is a "good" solution for this?
Help is appreciated <3
I didnt find any solution yet. Instead of "ignoring" the test-values, i went with "improved" seeding for the IDs.
Really hard, by setting them in a generic way with SpecimenBuilders (in AutoFixture), but anyhow it works.
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();
I have a weird situation where I have objects and Lists of objects as part of my entities and contracts to interface with a third-party service. I'm going to try to see if I can replace the actual object class with something more specific in the entities and contracts to get around this, but I am curious if there is a way to get AutoMapper to handle this as is.
Here are some dummy classes:
public class From
{
public object Item { get; set; }
}
public class FromObject
{
public string Value { get; set; }
}
public class To
{
public object Item { get; set; }
}
public class ToObject
{
public string Value { get; set; }
}
And the quick replication:
Mapper.CreateMap<From, To>();
Mapper.CreateMap<FromObject, ToObject>();
From from = new From { Item = new FromObject { Value = "Test" } };
To to = Mapper.Map<To>(from);
string type = to.Item.GetType().Name; // FromObject
Basically, the question is this: Is there a way to get AutoMapper to understand that from.Item is a FromObject and apply the mapping to ToObject? I'm thinking there's probably not a way to make it automatic, since there's nothing that would indicate that to.Item has to be a ToObject, but is there a way to specify during the CreateMap or Map calls that this should be taken into account?
I don't think there is an "automatic" way of doing it, since AutoMapper won't be able to figure out that From.Item is FromObject and To.Item is ToObject.
But, while creating mapping, you can specify that
Mapper.CreateMap<FromObject, ToObject>();
Mapper.CreateMap<From, To>()
.ForMember(dest => dest.Item, opt => opt.MapFrom(src => Mapper.Map<ToObject>(src.Item)));
From from = new From { Item = new FromObject { Value = "Test" } };
To to = Mapper.Map<To>(from);
string type = to.Item.GetType().Name; // ToObject
If you're willing to use an additional interface, this can be accomplished using Include. You can't just map object to object in this fashion, though.
public class From
{
public IItem Item { get; set; }
}
public class FromObject : IItem
{
public string Value { get; set; }
}
public class To
{
public object Item { get; set; }
}
public class ToObject
{
public string Value { get; set; }
}
public interface IItem
{
// Nothing; just for grouping.
}
Mapper.CreateMap<From, To>();
Mapper.CreateMap<IItem, object>()
.Include<FromObject, ToObject>();
From from = new From { Item = new FromObject { Value = "Test" } };
To to = Mapper.Map<To>(from);
string type = to.Item.GetType().Name; // ToObject
I have an object containing different properties like the object below:
public class CompressedLogResponse
{
public string LoggerType { get; set; }
public int NumberOfRegisters { get; set; }
public int NewLogId { get; set; }
public DateTime LoggerAnnounceTime { get; set; }
public List<Log> Log{ get; set; }
}
How can I return a List of this objekt that does not include the List<Log> Log property?
Linq would be preffered
Thanks for any help that you can provide
You cannot just hide a property of a class (you declared it a as public)
Option 1:
Althought as Robson wrote you can set it null (thats not very reliable thaught cause nobody expects a class containing a property that is always null)
Option2:
If you consume the class on the same place use a anonymous type as Mez wrote, althought it sounds like you want to hide the Property from external usage. (I don't like the dynamic approach, the dynamic keyword was made for interop/DOM not for transporting anonymous types.)
Option3:
If you want a List of this type to be returned without the Log property, you have to create a new class (Inheritance is a good way to realize this):
public class CompressedLogResponseBase
{
public string LoggerType { get; set; }
public int NumberOfRegisters { get; set; }
public int NewLogId { get; set; }
public DateTime LoggerAnnounceTime { get; set; }
}
public class CompressedLogResponse : CompressedLogResponseBase
{
public List<Log> Log{ get; set; }
}
Now you can return a list of base items (that do not have a Log property at all)
public List<CompressedLogResponseBase> ReturnWithoutLog(IEnumerable<CompressedLogResponse> items)
{
return ((IEnumerable<CompressedLogResponseBase>)items).ToList();
}
If a IEnumerable as return type is suficient it becomes really easy
public IEnumerable<CompressedLogResponseBase> ReturnWithoutLog(IEnumerable<CompressedLogResponse> items)
{
return items
}
whith "does not include the List Log property" i guess you mean that the property "public List Log" will be blanked but still there, so you can just null that property out, because if you create an object that doesn't contain the "public List Log" property, than it will not be a "CompressedLogResponse" but will be another type.
List<CompressedLogResponse> listOne = new List<CompressedLogResponse>();
//....
//fill the listOne
//....
List<CompressedLogResponse> listWithoutListLog = (from item in listOne
select new CompressedLogResponse(
LoggerType = item.LoggerType,
NumberOfRegisters = item.NumberOfRegisters ,
NewLogId = item.NewLogId ,
LoggerAnnounceTime = item.LoggerAnnounceTime ,
Log= null)).ToList();
You can return an anonymous list of your original list like the following;
public static List<dynamic> Test() {
List<CompressedLogResponse> list = new List<CompressedLogResponse>();
var result = list.Select(x => new
{
x.LoggerAnnounceTime,
x.LoggerType,
x.NewLogId,
x.NumberOfRegisters
});
return result.ToList<dynamic>();
}
Take a look at the .Select(), and also the dynamic keyword.
Then to call it,
var x = Test();
foreach(dynamic o in x)
{
int NumberOfRegisters;
//You have 2 ways... either by
NumberOfRegisters = o.NumberOfRegisters;
// or reflection
NumberOfRegisters = o.GetType().GetProperty("NumberOfRegisters").GetValue(o, null);
}
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?