I have a source model that looks like this:
public class FooBar {
public Foo foo {get;set;}
public Bar bar {get;set;|
}
public class Foo {
// tons of properties
}
public class Bar {
// tons of irrelevant properties
public Baz baz {get;set;}
}
Then I have a destination model that flattens FooBar into NewFooBar and looks like this:
public class NewFooBar {
// all properties from Foo
public Baz baz {get;set;}
}
How can I map FooBar to NewFooBar without having to individually map all of foo's properties? (there are many of them).
My first solution was to Map Foo and Bar seperately to NewFooBar, and that works. Then perform the maps individually. But this seems kludgy. I'd rather do it in a single mapping if possible. Am I missing some simple way to do this?
Would this work?
Mapper.CreateMap<Foo, NewFooBar>()
.ForMember(dest => dest.baz, opt => opt.Ignore())
;
Mapper.CreateMap<FooBar, NewFooBar>()
.ConvertUsing(src => {
var newFooBar = AutoMapper.Mapper.Map<Foo, NewFooBar>(src.foo);
newFooBar.baz = src.bar.baz;
return newFooBar;
});
;
and then
var newFooBar = AutoMapper.Mapper.Map<FooBar, NewFooBar>(fooBar);
Related
I have simple class like that;
class Foo {
constructor() {
this.datas = {}
}
set(key, data) {
return this.datas[key] = data
}
get(key) {
return this.datas[key]
}
}
module.exports = Foo
I am adding some data to datas veriable first. But when I call same class in the next time, veriable is not saving like that;
const foo1 = Foo()
foo1.set('a',[1,2,3])
const foo2 = Foo()
var aData = foo2.get('a')
console.log(aData)
But data not getting. How can I fix it?
The datas property that you defined in the Foo class is not being saved between instances of the class, because it is defined inside the constructor function. This means that every time you create a new Foo object with const foo = new Foo(), a new datas property will be created for that object, and it will not be shared with other instances of the Foo class.
if you want to shared by all instances of the class.refer Javascript ES6 shared class variable
You can pass your object into another class constructor,
https://stackblitz.com/edit/node-k3vqtp?file=index.js
or use global variable
global.foo = new Foo();
global.foo.set('a', [1, 2, 3]);
or use package like InversifyJS to inject the class
...
#injectable()
export default class A implements IA {
private _foo!: Foo;
public get foo(): Foo {
return this._foo;
}
public set foo(v: Foo) {
this._foo = v;
}
constructor(
#inject(TYPES.Foo) foo: Foo,
) {
this.foo = foo;
}
...
Due to the limited information from your question, here only list some options. You can look for the best way to fit your scenario.
How do I configure the mapper so that this works?
(i.e. the properties from the dynamic object should map to the properties of the class definition with the same letters - ignoring case)
public class Foo {
public int Bar { get; set; }
public int Baz { get; set; }
}
dynamic fooDyn = new MyDynamicObject();
fooDyn.baR = 5;
fooDyn.bAz = 6;
Mapper.Initialize(cfg => {});
Foo result = Mapper.Map<Foo>(fooDyn);
result.Bar.ShouldEqual(5);
result.Baz.ShouldEqual(6);
If your dynamic object implements IDictionary<string,object> (e.g. ExpandoObject) then the following should work. There must be some easier way to do this as anonymous objects are mapped just fine even if the case is different.
Mapper.Initialize(cfg =>
{
cfg.CreateMap<IDictionary<string, object>, Foo>()
.ConstructUsing(d =>
{
Foo foo = new Foo();
foreach (System.Reflection.PropertyInfo prop in typeof(Foo).GetProperties())
{
foreach (KeyValuePair<string, object> entry in d)
{
if (entry.Key.ToLowerInvariant() == prop.Name.ToLowerInvariant())
{
prop.SetValue(foo, entry.Value);
break;
}
}
}
return foo;
});
});
AutoMapper allows you to configure explicit member mapping on the map configuration in this style:
var config = new MapperConfiguration(cfg =>
{
var dynamicMap = cfg.CreateMap<IDictionary<string, object>, SomethingDTO>();
dynamicMap.ForAllMembers((expression) => expression.MapFrom(source =>
source.ContainsKey(expression.DestinationMember.Name.Substring(0, 1).ToLower()
+ expression.DestinationMember.Name.Substring(1))
? source[expression.DestinationMember.Name.Substring(0, 1).ToLower()
+ expression.DestinationMember.Name.Substring(1)] : null
));
});
For mapping a dynamic/expando object that is camel case to an type with pascal case members you could use ForAllMembers on the the explicit map configuration. Possible use case: json payloads to DTO.
In comparison to the other answer (which also works) this approach allows you to continue to benefit and use all the other map features and configuration.
I have a class (many more fields than defined below, but you get the basic idea):
public class Embedded
{
public int Field1{get;set;}
}
public class Source
{
public int Field2{get;set;}
public Embedded Embedded{get;set;}
}
public class Destination
{
public int Field1{get;set;}
public int Field2{get;set;}
}
The normal approach would be:
Mapper.Initialise(cfg=>
{
cfg.CreateMap<Source, Destination>(dest=>dest.Field1, opt=>opt.MapFrom(src=>src.Embedded.Field1));
}
My Embedded object has many fields (and I have multiple embedded objects) and they will map by convention to the fields in the Destination object.
I need something like the functionality provided by IncludeBase<> but to be able to specify which field should be used to use as the src.
Is there a simpler way of doing this?
I found Map<,>(s,d) and AfterMap:
Mapper.Initialize(cfg=>{
cfg.CreateMap<Embedded, Destination>();
cfg.CreateMap<Source, Destination>()
.AfterMap((s,d) {
Mapper.Map(s.Embedded, d);
}
});
var src = new Source{
Embedded = new Embedded();
}
var dest = Mapper.Map<Source, Destination>(src);
In Xamarin Android, is it possible to create an instance of an object from the class name?
For example if my class is Foo, I would like to do this:
ObjectHandle handle = (Foo)System.Activator.CreateInstance(null, "Foo");
Foo foo = (Foo)handle.Unwrap();
foo.PartyOn();
The class Foo is in the same project (the main App) which is why I'm passing null to CreateInstance.
This code produces a TypeLoadException: "Could not load type 'Foo'.
You need to qualify the namespace for Foo:
namespace MyApp
{
public class Foo
{
public void PartyOn()
{
Console.WriteLine ("Party party!");
}
}
// ...
var handle = Activator.CreateInstance(null, "MyApp.Foo");
Foo foo = (Foo)handle.Unwrap ();
foo.PartyOn();
}
I would like to ignore certain properties when mapping deep (ie levels > 1) object models.
The following test works fine:
class Foo
{
public string Text { get; set; }
}
class Bar
{
public string Text { get; set; }
}
Mapper.CreateMap<Foo, Bar>()
.ForMember(x => x.Text, opts => opts.Ignore());
var foo = new Foo { Text = "foo" };
var bar = new Bar { Text = "bar" };
Mapper.Map(foo, bar);
Assert.AreEqual("bar", bar.Text);
However when I try to do the same mapping but have Foo and Bar as properties on a parent class the following test fails:
class ParentFoo
{
public Foo Child { get; set; }
}
class ParentBar
{
public Bar Child { get; set; }
}
Mapper.CreateMap<ParentFoo, ParentBar>();
Mapper.CreateMap<Foo, Bar>()
.ForMember(x => x.Text, opts => opts.Ignore());
var parentFoo = new ParentFoo
{
Child = new Foo { Text = "foo" }
};
var parentBar = new ParentBar
{
Child = new Bar { Text = "bar" }
};
Mapper.Map(parentFoo, parentBar);
Assert.AreEqual("bar", parentBar.Child.Text);
Instead of ignoring the text of the Child class (ie left it as "bar") automapper sets the value to null. What am I doing wrong with my mapping configuration?
There are two ways Automapper can perform the mapping. The first way is to simply give Automapper your source object and it will create a new destination object and populate everything for you. This is the way most apps use Automapper. However, the second way is to give it both a source and an existing destination and Automapper will update the existing destination with your mappings.
In the first example, you're giving it an existing destination value so Automapper will use that. In the second example, Automapper is going to do the mapping for ParentFoo and ParentBar, but when it gets to the Child, it's going to create a new Child and then do the mapping (this is the default behavior). This results in the Text property being null. If you want to use the existing Child object, you'll need to configure Automapper to do that with UseDestinationValue:
Mapper.CreateMap<ParentFoo, ParentBar>()
.ForMember(b => b.Child, o => o.UseDestinationValue());
This makes your test pass (as long as you get rid of the first space when setting the parentBar.Child.Text!).