I need to create a mapping (using AutoMapper) from n classes all being derived from one abstract class to a contract class
So for example:
public abstract class bar
{
public string Field1 {get; set;}
public someClass Field2 {get; set;}
}
public class foo1bar: bar
{
// members
}
public class foo2bar: bar
{
// members
}
public class barContract
{
public string Field1 {get; set;}
// this will use existing someClass.Description field
public string Field2Description {get; set;}
}
implementations of bar class are multiple, and also are likely to change (more will be added). As Automapper cannot map to abstract class (so the constructor mapperConfiguration.CreateMap<bar, barContract>() is incorrect), I was wondering will it be possible to use reflection to find all classes 'implementing' bar class and map them 'automatically'
var type = typeof(bar);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));
I've got the Types, and I'm trying to invoke CreateMap.
As the type is now a variable, I'm creating a generic method once again using reflection:
foreach (Type t in types)
{
mapperConfiguration.GetType().GetMethod("CreateMap")
.MakeGenericMethod(t, typeof(barContract))
.Invoke(mapperConfiguration, null);
}
The problem is that CreateMap is not a member of type that is extracted from mapperConfiguration instance - when I'm trying to extract the method by name I get null. I see it's defined in IProfileExpression, so I'm trying to extract the method from the interface:
typeof(IProfileExpression).GetMethod("CreateMap") and I get System.Reflection.AmbiguousMatchException - what is kind of OK, but using System.Reflection.BindingFlags in GetMethod to be more specific I'm again getting nulls.
What am I doing wrong, or how to get around that mapping problem ?
You can create map from one type to another type CreateMap(SouceType, DestinationType));
public abstract class Bar
{
public string Field1 { get; set; }
}
public class Foo1bar : Bar
{
// members
}
public class Foo2bar : Bar
{
// members
}
public class BarContract
{
public string Field1 { get; set; }
// this will use existing someClass.Description field
public string Field2Description { get; set; }
}
class Program
{
static void Main(string[] args)
{
AutoMapperConfiguration.Init();
var foo1 = new Foo1bar {Field1 = "One"};
var barContract1=AutoMapperConfiguration.Mapper.Map<Foo1bar, BarContract>(foo1);
Console.WriteLine("barContract1.Field1: " + barContract1.Field1);
var foo2 = new Foo2bar {Field1 = "Two"};
var barContract2=AutoMapperConfiguration.Mapper.Map<Foo2bar, BarContract>(foo2);
Console.WriteLine("barContract2.Field1: " + barContract2.Field1);
Console.ReadLine();
}
public static class AutoMapperConfiguration
{
public static void Init()
{
MapperConfiguration = new MapperConfiguration(cfg =>
{
var types = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => !string.IsNullOrEmpty(type.Namespace) &&
type.BaseType != null &&
type.BaseType == typeof(Bar));
foreach (Type type in types)
{
cfg.CreateMap(type, typeof(BarContract));
}
});
Mapper = MapperConfiguration.CreateMapper();
}
public static IMapper Mapper { get; private set; }
public static MapperConfiguration MapperConfiguration { get; private set; }
}
}
Output
Related
I have classes something like this:
public class foo{
public string FooProp1 {get; set;}
public Bar Bar{get; set;}
}
public class Bar{
public string BarProp1 {get; set;}
public string BarProp2 {get; set;}
}
I have some audit setup where If I Update Foo then I can get that property Name and value for all the property apart from the 'Bar'. Is there a way to get property name and value of 'BarProp1'.
private void ProcessModifiedEntries(Guid transactionId) {
foreach (DbEntityEntry entry in ChangeTracker.Entries().Where(t => t.State == EntityState.Modified).ToList()) {
Track audit = CreateAudit(entry, transactionId, "U");
foreach (var propertyName in entry.CurrentValues.PropertyNames) {
string newValue = entry.CurrentValues[propertyName]?.ToString();
string originalValue = entry.OriginalValues[propertyName]?.ToString();
SetAuditProperty(entry, propertyName, originalValue, audit, newValue);
}
}
}
I want to audit BarProp1 when Foo got changed.
You want classes to report additional information to your auditing system. I think the best place to do that is in your CreateAudit method. The question is how.
You could have code in there that does something special for each incoming entry:
var foo = entry.Entity as Foo;
if (foo != null)
{
// do something with foo.Bar
}
var boo = entry.Entity as Boo;
if (boo != null)
{
// do something with boo.Far
}
etc.
Of course that isn't very pretty.
If you have multiple classes that need to report additional info to the auditor I would define an interface and tack that to each of these classes:
public interface IAuditable
{
string AuditInfo { get; }
}
public class Foo : IAuditable
{
public string FooProp1 { get; set; }
public Bar Bar { get; set; }
[NotMapped]
public string AuditInfo
{
get { return Bar?.BarProp1; }
}
}
And then in CreateAudit:
var auditable = entry.Entity as IAuditable;
if (auditable != null)
{
// do something with auditable.AuditInfo
}
And even if there's only one class that needs this behavior I would still use the interface because it makes your code self-explanatory.
Given the following:
public class Foo
{
public Int32 Foo_PK { get; set; }
public String SomeProperty { get; set; }
}
public class Bar
{
public Int32 Bar_PK { get; set; }
public Int32 Foo_FK { get; set; }
public String SomeOtherProperty { get; set; }
}
public class JoinResult<TEntity, TJoiningEntity>
{
public TEntity From { get; private set; }
public TEntity To { get; private set; }
public JoinResult(TEntity from, TEntity to)
{
this.From = from;
this.To = to;
}
}
public interface IFooResult
{
public String SomeProperty { get; set; }
}
public interface IBarResult : IFooResult
{
public String SomeOtherProperty { get; set; }
}
public class FooResultDTO : IFooResult, IBarResult
{
public String SomeProperty { get; set; }
public String SomeOtherProperty { get; set; }
}
The idea behind this is that we some method of dispensing foo's and foo's with other related records, e.g. if there are 4 bar's then 4 rows in a table with the additional fields.
public class FooDispensary
{
public IQueryable<T> Dispense<T>()
where T: IFooResult
{
using (var repository = new Repository())
{
// TODO: Handle mapping for Foo -> FooResult
// Project to
return repository.Foos.ProjectTo<FooResultDTO>();
}
}
public IQueryable<T> DispenseWithBars<T>()
where T : IFooResult, IBarResult
{
using (var repository = new Repository())
{
// TODO: Handle mapping for JoinResult.From (same as Foo -> FooResult) as well as to JoinResult.To
// Project to
return repository.Foos.Join((f) => f.Foo_PK,
(b) => b.Foo_FK,
(f, b) => new JoinResult<Foo, Bar>(f, b))
.ProjectTo<FooResultDTO>();
}
}
}
However, I would ideally like to only specify the base mapping once (Foo -> IFooResult) and then re-use this in the methods where we need to join to a child table.
There are multiple reasons behind wanting to do this which are specific to my project however no need to go into them, I am just wondering if this is possible as I have struggled with the syntax thus far?
Thanks
Create a Map between Foo and FooResult. Because the Property SomeProperty is named the same in both the source and target Automapper will be able to figure out the mapping implicitly.
// TODO: Handle mapping for Foo -> FooResult
AutoMapper.Mapper.CreateMap<Foo, FooResult>();
Then create a map between JoinResult<Foo, Bar> and FooResultDTO
// TODO: Handle mapping for JoinResult.From (same as Foo -> FooResult) as well as to JoinResult.To
AutoMapper.Mapper.CreateMap<JoinResult<Foo, Bar>, FooResultDTO>()
.ForMember(r => r.SomeProperty, opt => opt.MapFrom(f => f.From.SomeProperty)
.ForMember(r => r.SomeOtherProperty, opt => opt.MapFrom(f => f.To.SomeOtherProperty)
However, I would ideally like to only specify the base mapping once (Foo -> IFooResult) and then re-use this in the methods where we need to join to a child table.
You're not resusing the mapping between Foo and IFooResult anywhere in your example. Your second function needs to map between JoinResult<Foo, Bar> and FooResultDTO as shown above. If you need to reuse mappings I suggest you look into using an AutoMapper Profile and managing a singleton AutoMapper instance that can be shared between your functions: https://github.com/AutoMapper/AutoMapper/wiki/Configuration
I'd like to create a generic method to get glass casted items of T.
What I have so far is:
private static List<T> GetChildren<T>(Item parentItem) where T: class {
var lstChildItems = parentItem.Children.Where(child => child.TemplateID.Equals(T.TemplateId)).ToList();
List<T> lstChildren = lstChildItems.Select(c => c.GlassCast<T>()).ToList();
return lstChildren;
}
In my example T.TemplateId can't be resolved because T is only marked as class. Does TemplateId exist in some kind of interface or what do I have to enter instead of class?
If you want to get the TypeConfiguration:
var ctx = new SitecoreContext();
var typeConfig = ctx.GlassContext.TypeConfigurations[typeof(T)];
var templateId = (config as SitecoreTypeConfiguration).TemplateId;
//ofc check for nulls, but you get the point
But I personally like to utilize the InferType possibilities:
public interface ISitecoreItem
{
[SitecoreChildren(InferType = true)]
IEnumerable<ISitecoreItem> Children { get; set; }
}
[SitecoreType]
public class News : ISitecoreItem
{
public string Title { get; set; }
public virtual IEnumerable<ISitecoreItem> Children { get; set; }
}
private static IEnumerable<T> GetChildren<T>(this Item parentItem) where T : ISitecoreItem
{
var parentModel = item.GlassCast<ISitecoreItem>();
return parentModel.Children.OfType<T>();
}
//usage:
var newsItems = parentItem.GetChildren<News>();
The InferType option will give you the most specific available Type that Glass can find. So anything deriving from ISitecoreItem can be fetched like this.
I've just started with AutoMapper in C#. I've succesfully created a mapping like this:
Mapper.CreateMap<InputTypeA, OutputTypeA>()
I've also found a way to add some logic to specific properties, like formatting a date (in InputTypeA) to a string in a specific format (in OutputTypeA).
.ForMember(
dest => dest.MyDateProperty,
opt => opt.ResolveUsing(
src => String.Format("{0:yyyy-MM-dd}", src.MyDateProperty)));
Now I need to do the same for a number of float properties, but I'm wondering if there is a short/easy way to do this, except copying a piece of code like the one above for every property that needs to follow this rule.
I've found that I can create a new map like this for mapping floats to strings:
Mapper.CreateMap<float,string>()
.ConvertUsing(src =>
String.Format(CultureInfo.InvariantCulture.NumberFormat, "{0:0.00}", src));
This works, but is too generic, because I also have a mapping for another type (let's call it InputTypeB), that also contains float properties, which need to be treated differently.
Mapper.CreateMap<InputTypeB, OutputTypeB>()
How can I make the float-to-string mapping part of the first mapping only?
You could create two separate mappers based on two separate configurations, only one of which includes the float-to-string mapping:
public class InputTypeA { public float Foo { get; set; } }
public class OutputTypeA { public string Foo { get; set; } }
public class InputTypeB { public float Bar { get; set; } }
public class OutputTypeB { public string Bar { get; set; } }
public class Program
{
public static void Main(string[] args)
{
Func<float, string> mapFunc =
src => String.Format(CultureInfo.InvariantCulture.NumberFormat, "{0:0.00}", src);
var floatToStringConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<InputTypeA, OutputTypeA>();
cfg.CreateMap<float, string>().ConvertUsing(mapFunc);
});
var regularConfig = new MapperConfiguration(cfg =>
{
cfg.CreateMap<InputTypeB, OutputTypeB>();
});
IMapper floatToStringMapper = floatToStringConfig.CreateMapper();
IMapper regularMapper = regularConfig.CreateMapper();
var aIn = new InputTypeA() { Foo = 1f };
var aOut = floatToStringMapper.Map<OutputTypeA>(aIn);
Console.WriteLine(aOut.Foo); // prints "1.00"
var bIn = new InputTypeB() { Bar = 1f };
var bOut = regularMapper.Map<OutputTypeB>(bIn);
Console.WriteLine(bOut.Bar); // prints "1"
}
}
You can create custom value resolvers for each case you need to handle. Then apply these to the appropriate members in your mappings.
As an example, I need to map from TypeA to TypeB, I only want DateB to use the custom conversion:
public class TypeA {
public DateTime DateA { get; set; }
public DateTime DateB { get; set; }
}
public class TypeB {
public string DateA { get; set; }
public string DateB { get; set; }
}
I create a custom resolver:
class DateStringResolver : ValueResolver<DateTime, string> {
protected override string ResolveCore(DateTime source) {
return String.Format("{0:yyyy-MM-dd}", source);
}
}
Then in my mapper config:
Mapper.CreateMap<TypeA, TypeB>()
//Only Date B will use our custom resolver
.ForMember(d => d.DateB, opt => opt.ResolveUsing<DateStringResolver>().FromMember(src => src.DateA));
The resolver can now be applied wherever it is needed.
Docs:https://github.com/AutoMapper/AutoMapper/wiki/Custom-value-resolvers
I have an object of type Product and type Variant. Variant and Product have the same structure, but are two different types, and so I can't make just one method to encompass both. Is it possible to make an extension method that would accept both of these types?
You cannot do that unless Product and Variant have common base class or interface.
If they have common base class or interface then you can try to write extension method for it e.g.
public static void MyMethod(this ICommonInterface obj) {}
Otherwise you'll have to create two separate extensions methods. You can try to extract common code to a separate method which will be called from your extension methods.
Steve,
Two possible answers to this question from a Commerce Server perspective. Depends on whether you are talking about the Core Systems API or the Commerce Foundation API. I'll address both below:
OPTION 1 : Commerce Server Core Systems API
Unfortunately, the two classes Product and Variant DO NOT share a common base class (unless you count System.Object ;-). Although Microsoft.CommerceServer.Catalog.Product inherits from Microsoft.CommerceServer.Catalog.CatalogItem, Variant does not inherit from CatalogItem or any other common base class. Variant inherits directly from System.Object.
Therefore, you cannot write an extension method that can be leveraged by both classes.
The class definition documentation for all Catalog System objects (for the Core Systems API) can be found here http://msdn.microsoft.com/en-us/library/microsoft.commerceserver.catalog(v=cs.70).aspx
OPTION 2 : Commerce Foundation API
If you are referring to Product and Variant in terms of objects being returned from calls to the Commerce Foundation, then ALL objects returned by requests using the Commerce Server Operation Service are returned as type ICommerceEntity.
If you are using concrete data types to wrap CommerceEntity, then you can still use the .ToCommerceEntity() method and you could write your extension from ICommerceEntity. The only problem with that approach is ALL classes inheriting from ICommerceEntity would be able to use the extension methods.
View http://msdn.microsoft.com/en-us/library/dd451701.aspx for more on concrete data types in Commerce Foundation.
You could take advantage of the dynamic type:
using System.Linq;
class Program
{
static void Main(string[] args)
{
var myList = new object[] {
new Product(){Id=1},
new Variant(){Id=1}
};
Process(myList);
}
static void Process(object[] myList)
{
foreach (dynamic item in myList)
{
item.Id += 1;
}
}
}
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Foo { get; set; }
}
class Variant
{
public int Id { get; set; }
public string Name { get; set; }
public string Bar { get; set; }
}
pub
Using an extension method:
public static class X
{
public static void Process(this object[] myList)
{
foreach (dynamic item in myList)
{
item.Id += 1;
}
}
}
And the sample usage:
myList.Process();
Another alternative
... is to use an object-object mapper such as AutoMapper. For the sample Product and Variant sample types above, you would have to define the members mapping with the following type which shares the common members of the 2 classes:
public class MyMapper
{
public int Id { get; set; }
public string Name { get; set; }
}
The members mapping looks like this:
Mapper.CreateMap<Product, MyMapper>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name));
Mapper.CreateMap<Variant, MyMapper>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name));
And you might get a working List<MyMapper> like this:
var myMapperList = myList.Select(item => item.Map()).ToList();
The Map<T> extension method:
public static MyMapper Map<T>(this T obj)
{
return (MyMapper)Mapper.Map(obj, obj.GetType(), typeof(MyMapper));
}
From this point further, you would define your extension methods for the MyMapper type.
You can not change the Product and Variant classes, but you can subclass them and apply a interface to the subclass. This interface can be used for the extension method:
using System;
public class Program
{
public static void Main()
{
var p = new MyProduct();
p.Name = "test";
Console.WriteLine(p.GetName());
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Foo { get; set; }
}
public class Variant
{
public int Id { get; set; }
public string Name { get; set; }
public string Bar { get; set; }
}
public class MyProduct: Product, IType {
}
public class MyVariant: Variant, IType {
}
public static class Extensions {
public static string GetName(this IType type){
return type.Name;
}
}
public interface IType {
string Name {get; set; }
}
See: https://dotnetfiddle.net/u1JJmJ