Automapper flattening of nested object child class - c#

I am trying to perform the following operation with AutoMapper and don't get the expected result.
I have the following configuration:
public interface IFlatInterface
{
public string A {get; set;}
public string B {get; set;}
public string C {get; set;}
public bool D {get; set;}
}
public class ParentObject
{
public Data X {get; set;}
public Measure Y {get; set;}
}
public class Data
{
public string A {get; set;}
public string B {get; set;}
}
public class Measure
{
public string C {get; set;}
public bool D {get; set;}
}
I have defined the following mapping rules
cfg.CreateMap<IFlatInterface, ParentObject>()
.ForMember(p => p.Measure, o => o.MapFrom(f => f))
.ForMember(p => p.Data, o => o.MapFrom(f => f))
cfg.CreateMap<ParentObject, IFlatInterface>()
.ForMember(i => i.A, o => o.MapFrom(p => p.X.A))
.ForMember(i => i.B, o => o.MapFrom(p => p.X.B))
.ForAllOtherMembers(o => o.MapFrom(p.Y))
The issue is that the value which is populated is a string "p.Measure" for all the remaining fields. I would like to mapping to happen recursively on the members of Measure. I can't put a extensive rule because different classes can implement IFlatInterface.
Any idea on how to achieve this?
Thanks

Related

Automapper define a custom variable used to determine the values of several properties from destination

I am using AutoMapper to map some DTOs to ui Models. However, one of my DTOs contains a field with serialized data (the serialized data represents another object with properties) that is used to determine the values of several properties of the model.
For example:
public class SourceDTo
{
public int Id {get; set;}
public int Name {get; set;}
public string SerializedDetails {get; set;}
}
public source DestinationModel
{
public int Id {get; set;}
public int Name {get; set;}
public string Address {get; set;}
public string AccountNr {get; set;}
}
So in this case, when mapping the source to destination, I would like to be able to deserialize the SerializedDetails field first and then use it in order to map Address and AccountNr with properties from that object, instead of deserializing it twice, for each property. Can this be done with AutoMapper?
May not be the most elegant solution YMMV:
CreateMap<Foo, Bar>()
.ForMember(dest => dest.prop1
, opt => opt.MapFrom((src, dest, destMember, context) => context.GetJsonObjectValue<string>("RawMessageJSON", "$.props.prop1")))
.ForMember(dest => dest.prop2
, opt => opt.MapFrom((src, dest, destMember, context) => context.GetJsonObjectValue<int>("RawMessageJSON", "$.props.prop2")));
Helper Extension Method for extracting Json path values from the JObject:
public static class AutomapperResolutionContextExtensions
{
public static T GetJsonObjectValue<T>(this ResolutionContext context, string key, string jsonTokenPath)
=> ((JObject)context.Items[key]).SelectToken(jsonTokenPath).ToObject<T>();
}
Here's the classes we are mapping:
public class Foo
{
public string RawMessageString { get; set;}
[NotMapped]
public JObject RawMessageJSON => JObject.Parse(RawMessageString);
}
public class Bar
{
public string prop1 { get; set; }
public int prop2 { get; set; }
}
When calling Map() we deserialize the string to a JObject and pass it in through in the Items Dictionary:
var bar = mapper.Map<Bar>(foo, opt => opt.Items["RawMessageJSON"] = foo.RawMessageJSON);

Best way to return count of nested objects without list them

I need to count a list of nested objects, but don't want to return the nested objects.
I have the classes:
public class Categoria
{
public int ID {get; set}
public List<Produto> produto {get; set;}
public int produtoCount {get; set;}
}
public class Produto
{
public ID {get; set}
public string data {get; set;}
}
I have tried using produtoCount in Categoria class, but it only has value when I use a Include of Produto class, like this:
(new Categoria()).AsQueryable().Include(p => p.Produto).ToList();
Is it possible?
Sure it's possible:
DbContext.Categorias
.Where(c => c.Id == id)
.Select(c => new
{
// Assuming you also want the Categoria
Categoria = c,
ProdutoCount = c.produto.Count()
})
.FirstOrDefault();
Please try below code. It may help you.
public class Categoria
{
public int ID {get; set}
private List<Produto> produto {get; set;}
public int produtoCount {get {return produto.Count();} }
}
public class Produto
{
public ID {get; set}
public string data {get; set;}
}
Thanks,
Amit Prajapati

Automapper mapping clarification to satisfy design

I am currently working with a DB table and I need to map extra properties from DAO to BE class.
I have to produce XML like the following:
<Zoos>
<Zoo>
<ZooId>234OI456<ZooId>
<Name>The Zoo</Name>
<Address>3456 Kramer</Address
<ZipCode></ZipCode>
<Animals>
<Animal Type="REPTILE">Cobra</Animal>
<Animals>
<Zoo>
<Zoos>
The extra columns in the db view are like so:
ANI_TYPE VARCHAR(20)
ANI_VALUE VARCHAR(20)
Previously when I didn't have the extra columns I mapped the values like this for each Zoo
Mapper.CreateMap<ZooDAO, ZooBE>()
.ForMember(d => d.ZooId, e => e.ZOO_ID))
.ForMember(d => d.Name, e => e.ZOO_NAME))
.ForMember(d => d.Address, e => e.ZOO_ADDRESS))
.ForMember(d => d.ZipCode, e => e.ZOO_ZIPCODE));
How would I go about mapping these 2 columns(ANI_TYPE, ANI_VALUE) so i can create the structure shown in the xml in regards to Animals?
I have a c# class for Animal type which has the following
public enum AnimalType
{
INVALID,
REPTILE,
MAMMAL,
INSECT
}
My C# class for the Animal looks like this but i guess it will require rework. Feel free to provide suggestions/examples:
Currently this is what my classes look like:
//BE class
public class Zoo
{
public int ZooId {get; set;}
public string Name { get; set; }
public string Address {get; set; }
public string ZipCode {get; set;}
public List<Animal> Animals {get; set;}
}
//BE Class
public class Animal
{
public string Type {get; set;}
public string Text { get; set; }
}
My DAO class
public class ZooDAO
{
public int ZOO_ID {get; set;}
public string ZOO_NAME { get; set; }
public string ZOO_ADDRESS {get; set; }
public string ZOO_ZIPCODE {get; set;}
public string ANI_TYPE {get; set;}
public string ANI_VALUE {get; set;}
}
I appreciate if someone can assist me with the above.
Thanks,

Eager loading nested entities

I have the following (kept really simple):
public class Customer
{
public int Id {get; set}
public IEnumerable<Reminder> Reminders {get; set}
public IEnumerable<Order> Orders {get; set}
}
public class Reminder
{
public int CustomerId {get; set}
public string Text {get; set}
}
public class Order
{
public int CustomerId {get; set}
public int CategoryId {get; set}
}
public class OrderDetails
{
public int OrderId {get; set}
public int ProductId {get; set}
}
I'm also using generic repository pattern, and I load entities with nested entities as follows:
public virtual T Get(int id, params Expression<Func<T, object>>[] include)
{
if (include.Any())
{
var set = include.Aggregate<Expression<Func<T, object>>, IQueryable<T>>
(dbset, (current, expression) => current.Include(expression));
return set.SingleOrDefault<T>(x => x.Id == id);
}
return dbset.Find(id);
}
Using Linq, I would like to load a Customer, all his Reminders and all Orders (and their details) having a particular CategoryId. So I'm trying to do like this:
var customer = customerRepository.Get(10, x => x.Reminders, x => x.Orders.Select(o => o.OrderDetails));
But the above loads everything. And if I do the following, an exception is thrown:
var customer = customerRepository.Get(10, x => x.Reminders, x => x.Orders.Where(c => c.CategoryId == categoryId).Select(o => o.OrderDetials));
So how can I actually return only those orders I need?

AutoMapper Let Instance Variable

Apologies if this has been asked before, I'm not sure of the specific terms to ask.
A short example:
public class SignOffDetails
{
public int fUserID {get; set;}
public DateTime SignedAt {get; set;}
}
public class User
{
public int UserID {get; set;}
public string FullName {get; set;}
public Image Signature {get; set;}
}
public class MdlsCombined
{
public int UserID {get; set;}
public string FullName {get; set;}
public Image Signature {get; set;}
}
public void Program
{
Mapper.CreateMap<SignOffDetails, MdlsCombined>()
.ForMember(m => m.UserID, y => y.MapFrom(z => z.fUserID));
// Awesome
}
I know I can just map them individually, but is there a function to do the following that I haven't yet discovered?
Mapper.CreateMap<SignOffDetails, MdlsCombined>()
.Let(z => (User)Users.GetUser(z.fUserID))
.ForMember(m => m.UserID, y => y.MapFrom(z => z.fUserID))
.ForMember(m => m.FullName, y => y.MapFromLet(l => ((User)l).FullName))
.ForMember(m => m.Signature, y => y.MapFromLet(l => ((User)l).Signature));
I realize this is quite silly and kinda removes the whole point of procedural programming in the first place, but it's still a legit use imho.
The actual solution is to have two maps from SignOffDetails > MdlsCombined and User > MdlsCombined, but that ain't fluent ;)

Categories