Automapper mapping property unexpectedly by partial property name match - c#

I have two classes being mapped. The source class has a DateTime property that gets mapped to a destination property of type long (DateTime.Ticks), they are UpdateDt and UpdateDtTicks respectively.
When I use Automapper to map these two classes my UpdateDtTicks property automaticly gets the value from the UpdateDt property, even though the property names are not the same and I have not explicitly set the mapping for this property.
Is Automapper setting the property automatically because the property names only differ at the end? If not why is this happening as it is unexpected behavior.
Please see the code below:
static void Main(string[] args)
{
Configuration.Configure();
var person = new OrderDto
{
OrderId = 999,
MyDate = new DateTime(2015, 1, 1, 4, 5, 6)
};
var orderModel = Mapper.Map<OrderModel>(person);
Console.WriteLine(new DateTime(orderModel.MyDateTicks.Value));
Console.ReadLine();
}
public class OrderDto
{
public int OrderId { get; set; }
public DateTime MyDate { get; set; }
}
public class OrderModel
{
public int OrderId { get; set; }
public long? MyDateTicks { get; set; }
}
public class Configuration
{
public static void Configure()
{
Mapper.CreateMap<OrderDto, OrderModel>();
}
}
The result in the console:
And a watch:

You are triggering AutoMapper's flattening feature. Frome the documentation:
When you configure a source/destination type pair in AutoMapper, the configurator attempts to match properties and methods on the source type to properties on the destination type. If for any property on the destination type a property, method, or a method prefixed with "Get" does not exist on the source type, AutoMapper splits the destination member name into individual words (by PascalCase conventions).
Thus, given the following example (from their docs as well, shortened in my answer):
public class Order
{
public Customer Customer { get; set; }
}
public class Customer
{
public string Name { get; set; }
}
public class OrderDto
{
public string CustomerName { get; set; }
public decimal Total { get; set; }
}
Mapper.CreateMap<Order, OrderDto>();
var order = new Order
{
Customer = new Customer
{
Name = "John Doe"
}
};
OrderDto dto = Mapper.Map<Order, OrderDto>(order);
The CustomerName property matched to the Customer.Name property on Order.
This is exactly the same as MyDateTicks matching MyDate.Ticks, which returns a long as necessary...

Related

Elasticsearch NEST query nested object

I would like to retrieve a nested object from documents in my index called "userprofiles".
My UserProfile model:
public class UserProfileModel
{
public string FullName { get; set; }
public string Oid { get; set; }
public string Upn { get; set; }
public List<SsoLink> FavoriteSsoLinks { get; set; } = new List<SsoLink>();
}
My SsoLink Model:
public class SsoLink
{
public string Id { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Owner { get; set; }
}
Index creation:
PUT userprofiles
{
"mappings" : {
"properties" : {
"FavoriteSsoLinks" : {
"type" : "object"
}
}
}
}
My Query:
var searchResponse = _client.Search<UserProfileModel>(s => s
.Index(_profileIndex)
.Query(q=>q
.Term(t => t.Field(t => t.Oid).Value(oid)
)
)
);
Right now it returns the documents, but the favoritelinks object is blank, however I see objects listed from Kibana. I must be missing something obvious, but having trouble figuring this out.
Here is an example of my data:
Index creation example uses "FavoriteSsoLinks" as the property, but the Kibana screenshot uses "favoriteSsoLinks" starting with a lowercase f - which is correct? My suspicion is that property casing may be the issue, but would need to see the mapping in the index to know if this is correct.
The 7.x client is strict about property name casing in JSON, and by default uses camelcase property names to match to POCO property names. For example, by default
"favoriteSsoLinks" in JSON will be a match for FavoriteSsoLinks POCO property
"FavoriteSsoLinks" in JSON will not be a match for FavoriteSsoLinks POCO property
This behaviour can be changed with DefaultFieldNameInferrer(Func<string, string>) on ConnectionSettings for all properties, or on a property by property basis using attributes on properties such as DataMemberAttribute or PropertyNameAttribute, or using DefaultMappingFor<T> where T is the POCO type.

Map JSON column from MySql Database to C# Class from Web Api

I have a MySql database with columns Id int and Name:json
Places Table Sample
Id Name
1 {"en":"Sphinx","ar":"أبو الهول","fr":"Le sphinx"}
C# Place class
public class Place
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
I'm connecting with EntityFramework 6 and connection success and retrieve data like this
{Id = 1, Name = "{\"en\":\"Sphinx\", \"ar\":\"أبو الهول\", \"fr\":\"Le sphinx\"}" }
What I want how to Map Name to new Object not JSON string
something like this
Place class
public class Place
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public Localized<string> Name { get; set; }
}
Localized class
public class Localized<T>
{
public T en { get; set; } // english localization
public T ar { get; set; } // arabic localization
public T fr { get; set; } // french localization
}
when I do this Name property come with NULL value
Code in Repository
using (var context = new PlacesEntityModel())
{
return context.Places.Take(5).ToList();
}
I don't want to use AutoMapper,
I want something in EntityFramework to select only one language in Database Level without fetching all other data and then map it
how to fix this?
You can try extension method to map from your entity type.
public class Place
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
}
public class PlaceDTO
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public Localized<string> Name { get; set; }
}
public class Localized<T>
{
public T en { get; set; } // english localization
public T ar { get; set; } // arabic localization
public T fr { get; set; } // french localization
}
Extenstion Method ToDto
public static class Extensions
{
public static PlaceDTO ToDto(this Place place)
{
if (place != null)
{
return new PlaceDTO
{
Id = place.Id,
Name = JsonConvert.DeserializeObject<Localized<string>>(place.Name)
};
}
return null;
}
}
Usage
var place = new Place() { Id = 1, Name = "{\"en\":\"Sphinx\", \"ar\":\"أبو الهول\", \"fr\":\"Le sphinx\"}" };
var placeDTO = place.ToDto();
Console.WriteLine($"{placeDTO.Id}-{placeDTO.Name.ar}-{placeDTO.Name.en}-{placeDTO.Name.fr}");
First of all, by using a class with a property per language, you restrict yourself. You'd always have to add new properties if you add new languages, which would of course be feasible, but unnecessary complicated. Furthermore you'd usually have the language as a string-ish object (or be able to convert), hence this would lead to code like this
Localized<string> name = ...;
switch(language)
{
case "en":
return name.en;
case "ar":
return name.ar;
case "fr":
return name.fr;
default:
throw new LocalizationException();
}
which is error-prone and overly complicated. For your problem, I think I'd opt to use some kind of dictionary
IDictionary<string, string> names = ...;
if(names.ContainsKey(language))
{
return names[language];
}
else
{
throw new LocalizationException();
}
which is easily extensible by just adding more translations to the dictionary.
To convert your JSON string to an IDcitionary<string, string>, you could use the following code
localizedNames = JObject.Parse(Name)
.Children()
.OfType<JProperty>()
.ToDictionary(property => property.Name,
property => property.Value.ToString());
From within your class this would effectively be
public class Place
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
public Dictionary<string, string> LocalizedNames
{
get
{
return JObject.Parse(Name)
.Children()
.OfType<JProperty>()
.ToDictionary(property => property.Name,
property => property.Value.ToString());
}
}
}
The localized values can be accessed like
var localizedPlaceName = place.LocalizedNames[language];
Please note: Depending on your needs and use cases, you should consider the following issues:
Caching
In my snippet, the JSON string is parsed every time the localized names are accessed. Depending on how often you access it, this might be detrimental to performance, which could be mitigated by caching the result (don't forget to delete the cache when Name is set).
Separation of concerns
The class as is is supposed to be a pure model class. You might want to introduce domain classes that encapsulate the presented logic, rather than adding the logic to the model class. Having a factory that creates readily localized objects based on the localizable object and the language could be an option, too.
Error handling
In my code there is no error handling. Depending on the reliability of input you should consider additional error handling.
devart.com/dotconnect/mysql/docs/EF-JSON-Support.html
Like what #Nkosi said
In that case then, take a look at this article devart.com/dotconnect/mysql/docs/EF-JSON-Support.html
It probably can given that the library was able to build that feature in. You would need to figure out what they they did (reverse engineer)
I usually just use JSON.Net, I notice that another answer referenced JObject, but without going into whether your data-model is the right model, I generally find that you can do:
var MyObjectInstance = JObject.Parse(myJsonString).ToObject<MyObjectType>();
I notice that you have ComponentModel attributes on your class. I don't know off hand how many of these JSon.Net supports, and you'd have to research that. It definitely supports some attributes from XML serialization, and also has some of it's own.
Note that you can also convert a JSOn array into a list:
var MyObjectList = JArray.Parse(myJsonString).ToObject<IEnumerable<MyObjectType>();
I want something in EntityFramework to select only one language in
Database Level without fetching all other data and then map it
if you want it to be from database level, you can always create a view and then include this view in your project.
Example :
CREATE VIEW `PlacesLocalized` AS
SELECT
Id
, TRIM(REPLACE(name->'$.en', '"','')) AS en
, TRIM(REPLACE(name->'$.ar', '"','')) AS ar
, TRIM(REPLACE(name->'$.fr', '"','')) AS fr
FROM
places
This would create a model class Like :
public class PlacesLocalized
{
public int Id { get; set; }
public string en {get; set;}
public string ar {get; set;}
public string fr {get; set;}
}
Then, you can do :
var places = context.PlacesLocalized.Where(x=> x.en == "Sphinx");
But if you don't have enough permissions to do this in the database level, then you would need to specify the query in your EF. There is no easy way to change the execution logic of Entity Framework just for specific classes. That's why Entity Framework included SqlQuery method, which would give more flexibility to have custom queries when needed (like yours).
So, if you need to specify the localization from Entity Framework, then you would do a repository class to specify all custom queries you need including creating any DTO needed.
The basic way would be something like this :
public enum Localized
{
English,
Arabic,
French
}
public class PlaceRepo : IDisposable
{
private readonly PlacesEntityModel _context = new PlacesEntityModel();
public List<Place> GetPlacesLocalized(Localized localized = Localized.English)
{
string local = localized == Localized.Arabic ? "$.ar"
: localized == Localized.French ? "$.fr"
: "$.en";
return _context.Places.SqlQuery("SELECT Id, name-> #p0 as Name FROM places", new[] { local })
.Select(x=> new Place { Id = x.Id, Name = x.Name.Replace("\"", string.Empty).Trim() })
.ToList();
}
private bool _disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
_disposed = true;
}
}
~PlaceRepo()
{
Dispose(false);
}
}
now, you can do this :
using(var repo = new PlaceRepo())
{
var places = repo.GetPlacesLocalized(Localized.Arabic);
}
public class Place
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public string Name { get; set; }
public static explicit operator Place(PlaceDTO dto)
{
return new Place()
{
Id = dto.Id,
Name = dto.Name
};
}
}
public class PlaceDTO
{
[Key, Column("id")]
public int Id { get; set; }
[Column("name")]
public Localized<string> Name { get; set; }
public static explicit operator PlaceDTO(Place pls)
{
return new PlaceDTO()
{
Id = pls.Id,
Name = pls.Name
};
}
}
var placeDTO = (placeDto)place;
we can achieve this using explicit operator without using auto mapper

Assignment same property values to other

I have different classes sharing some properties of same type and name. I wish to assign same property values to each other. I explain my intention better in comments in the following pseudo-code. Is it possible in C#?
Ponder that there are a plethora of common properties but in unrelated classes, must we assign them one-by-one?
Second case is about sharing same properties but some of them may be nullable, who knows!
Side note: the classes already exist, cannot be altered, touched. Kinda sealed.
Can't it be done using nameofoperator and two for loops? Compare property names if matched, assign?
using System;
namespace MainProgram
{
class HomeFood
{
public DateTime Date { get; set; }
public string food1 { get; set; }
public string food2 { get; set; }
public int cucumberSize { get; set; }
}
class AuntFood
{
public string food2 { get; set; }
public int cucumberSize { get; set; }
public DateTime Date { get; set; }
public string food1 { get; set; }
// extra
public double? length { get; set; }
}
class GrandpaFood
{
public string? food2 { get; set; }
public int cucumberSize { get; set; }
public DateTime? Date { get; set; }
public string food1 { get; set; }
// extra
}
static class Program
{
public static void Main(string[] args)
{
var home = new HomeFood
{
Date = new DateTime(2020, 1, 1),
food1 = "cucumber",
food2 = "tomato",
cucumberSize = 123
};
var aunt = new AuntFood();
/*
First case: same types
Expected for-each loop
assigning a class's property values
to other class's property values
or for-loop no matter
foreach(var property in HomeFood's properties)
assign property's value to AuntFood's same property
*/
var home2 = new HomeFood();
var grandpa = new GrandpaFood
{
Date = new DateTime(2020, 1, 1),
food1 = "dfgf",
food2 = "dfgdgfdg",
cucumberSize = 43534
};
/*
Second case: similar to first case
with the exception of same type but nullable
or for-loop no matter
foreach(var property in GrandpaFood's properties)
assign property's value to GrandpaFood's same property
we don't care if it is null e.g.
Home2's same property = property's value ?? default;
*/
}
}
}
Based on the comments in the questions, this is just to show how it can be done with reflection.
Disclaimer, this is just a very simplified example on how to use reflection to sync properties. It does not handle any special cases (modifiers, read only, type mismatch, etc)
I would strongly suggest to use automapper to achieve the qp goals.
public class Type1
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class Type2
{
public string Property1 { get; set; }
public string Property3 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var t1 = new Type1 { Property1 = "Banana" };
var t2 = new Type2();
var properties1 = typeof(Type1).GetProperties().ToList();
var properties2 = typeof(Type2).GetProperties().ToList();
foreach(var p in properties1)
{
var found = properties2.FirstOrDefault(i => i.Name == p.Name);
if(found != null)
{
found.SetValue(t2, p.GetValue(t1));
}
}
Console.WriteLine(t2.Property1);
}
}
The short answer is, apply OOP. Define a base Food class and inherit from it in any specific food classes you have. You can put all the shared props in the base class.
public class Food
{
public string food2 { get; set; }
// other shared stuff
}
class GrandpaFood : Food
{
// other specific stuff
}
As others have said, use some of the Object Oriented properties, like inheriting a super class of implement an interface.
In case you go for inheritance, consider making the super class (the one you inherit from) abstract. This means that the super class itself cannot be instantiated, which greatly reduces the risk of violating the Liskov Substitutional Principle. Also it often reflects the real problem better. In your example, this would also be the case, as “food” is not an actual thing in the real world, but rather a group of things.

Automapper along with generics and mapping missing properties

I'm trying to use a generic mapper for mapping two objects. So I have setup Automapper in this way which comes from the documentation:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap(typeof(Source<>), typeof(Destination<>));
});
mapper = config.CreateMapper();
Now everything works well in the case if the source and destination have the same properties and if I'm going from more properties on the source side to less properties on the destination. However if the source has less properties than the destination than I get an error:
Value ---> AutoMapper.AutoMapperConfigurationException:
Unmapped members were found. Review the types and members below.
My question is there a way I could ignore these properties even though I won't know what they are at compile time?
Source:
public class Source<T>
{
public T Value { get; set; }
}
public class Destination<T>
{
public T Value { get; set; }
}
public class DataObject1
{
public int Id { get; set; }
public string Code { get; set; }
}
public class DataObject2
{
public int Id { get; set; }
public string Code { get; set; }
public string ActiveFlag { get; set; }
}
Here is my Test Code:
var data = new DataObject1() { Id = 10, Code = "Test" };
var source = new Source<DataObject1> { Value = data };
var dest = mapper.Map<Source<DataObject1>, Destination<DataObject2>>(source);
Assert.AreEqual(dest.Value.Id, 10);
Your mapping will succeed if you map DataObject1 to DataObject2 when you configure your mapper like so:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap(typeof(Source<>), typeof(Destination<>));
cfg.CreateMap<DataObject1, DataObject2>();
});
... or are you trying to avoid having to know at compile time that you may need to map DataObject1 to DataObject2 at all?

How to map some source properties to a wrapped destination type using AutoMapper?

Suppose you have this source model:
public abstract class SourceModelBase {
}
public class SourceContact : SourceModelBase {
public string FirstName { get; set; }
public string LastName { get; set; }
public KeyValuePair Pair { get; set; }
public SourceAddress Address { get; set; }
}
public class KeyValuePair { // Not derived from SourceModelBase.
public string Key { get; set; }
public string Value { get; set; }
}
public class SourceAddress : SourceModelBase {
public string StreetName { get; set; }
public string StreetNumber { get; set; }
}
Now the destination model should be mapped 1:1 by default (subject to normal AutoMapper configuration), but each thing derived from SourceModelBase should be mapped to a wrapper class class Wrap<T> { T Payload { get; set; } string Meta { get; set; } }.
public abstract class DestinationModelBase {
}
public class DestinationContact : DestinationModelBase {
public string FirstName { get; set; }
public string LastName { get; set; }
public KeyValuePair Pair { get; set; } // Not wrapped, base class not `SourceModelBase`.
public Wrap<DestinationAddress> Address { get; set; }
}
public class DestinationAddress : DestinationModelBase {
public string StreetName { get; set; }
public string StreetNumber { get; set; }
}
Since the contact class itself is derived from SourceModelBase it should be wrapped as well.
The result should have this structure:
Wrap<DestinationContact> Contact
string Meta // Comes from the custom wrapper logic.
DestinationContact Payload
string FirstName
string LastName
KeyValuePair Pair
string Key
string Value
Wrap<DestinationAddress> Address
string Meta // Comes from the custom wrapper logic.
DestinationAddress Payload
string StreetName
string StreetNumber
Obviously this wrapping should nest, illustrated by the fact that the mapped object itself is subject to it and so is its Address property.
For some reason all I keep finding are questions related to mapping from destination to source. I know I have to somehow use ResolveUsing and if the destination type is derived from SourceModelBase, somehow apply custom logic to provide the Wrap<T> value based on the value of the source property.
I don't know where to start at all, though. Especially when the source object itself is specified to be subject of the wrapping logic as well.
What's the best, most AutoMapper-idiomatic way to wrap the nested objects if they meet a condition and at the same time wrap the original object as well if it meets the same condition? I already have the mapper creation abstracted away so I can mold the original object automatically before passing it to the mapper, which may help with subjecting the original object to the resolver as well by doing mapper.Map(new { Root = originalObject }) so the resolver sees the instance of the original object as if it was a value of a property of source object as well, not the source object itself.
According to this issue on AutoMapper GitHub page, there is no direct way to do it.
But there is some workarounds. For example - reflection.
In this case you need to know wrapper type and implement converter for desired types. In this example it's MapAndWrapConverter from TSource to Wrap<TDestination>
CreateWrapMap method creates two bindings:
SourceAddress -> Wrap<DestinationAddress> and SourceContact -> Wrap<DestinationContact> which allow you to map SourceContant to wrapped DestinationContact.
internal class Program
{
public static void Main()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SourceAddress, DestinationAddress>();
cfg.CreateMap<SourceContact, DestinationContact>();
cfg.CreateWrapMap(
//func selecting types to wrap
type => typeof(DestinationModelBase).IsAssignableFrom(type)
&& !type.IsAbstract,
typeof(Wrap<>),
typeof(MapAndWrapConverter<,>));
});
var mapper = config.CreateMapper();
//Using AutoFixture to create complex object
var fixture = new Fixture();
var srcObj = fixture.Create<SourceContact>();
var dstObj = mapper.Map<Wrap<DestinationContact>>(srcObj);
}
}
public static class AutoMapperEx
{
public static IMapperConfigurationExpression CreateWrapMap(
this IMapperConfigurationExpression cfg,
Func<Type, bool> needWrap, Type wrapperGenericType,
Type converterGenericType)
{
var mapperConfiguration =
new MapperConfiguration((MapperConfigurationExpression)cfg);
var types = Assembly.GetExecutingAssembly().GetTypes();
foreach (var dstType in types.Where(needWrap))
{
var srcType = mapperConfiguration.GetAllTypeMaps()
.Single(map => map.DestinationType == dstType).SourceType;
var wrapperDstType = wrapperGenericType.MakeGenericType(dstType);
var converterType = converterGenericType.MakeGenericType(srcType, dstType);
cfg.CreateMap(srcType, wrapperDstType)
.ConvertUsing(converterType);
}
return cfg;
}
}
public class MapAndWrapConverter<TSource, TDestination>
: ITypeConverter<TSource, Wrap<TDestination>>
{
public Wrap<TDestination> Convert(
TSource source, Wrap<TDestination> destination, ResolutionContext context)
{
return new Wrap<TDestination>
{
Payload = context.Mapper.Map<TDestination>(source)
};
}
}
CreateWrapMap method is a little bit messy, especially the part with finding matching types. But it can be refined according to your needs.

Categories

Resources