medium trust support

May 24, 2011 at 3:32 PM

Just wondering if this library supports medium trust environments ?

Thanks!

Coordinator
May 24, 2011 at 4:12 PM

I'm not using Reflection.Emit so it should work

May 24, 2011 at 4:34 PM

Hi - I work with Shan. Thanks for the reply, just got a couple more:

- Do you have any perf tests anywhere vs AutoMapper or other common methods?

- Am I right in thinking the core architecture iterates through all the properties on the source & target objects and calls Match() for them? If so, is there any way of limiting this loop to go through a predetermined set of matches, or compile that set of matches a single time for the AppDomain and re-use them on subsequent injections?

Many thanks!

Alex

Coordinator
May 24, 2011 at 5:47 PM

hi,

yes I have one here http://valueinjecter.codeplex.com/SourceControl/changeset/view/66250#837322

I use there HyperDescriptor 

PropertyInfosStorage.RegisterActionForEachType(HyperTypeDescriptionProvider.Add); to make it super fast (but sometimes there are problems when using HyperTypeDescriptor)
but it's a "clean" automapper
probably if you call .CreateMap<> a lot of times HyperDescriptor wouldn't be necessary to be faster than automapper (but I've never tested this)

to not loop through all of the properties you could inherit directly from ValueInjection
or 
inherit the ConventionInjection and in Match(c) match the properties only by names and in SetValue check for types and depending on that set the value in different ways (but that's a custom scenario)

anyway, I never had complaints about speed, just use it in the easy way, and after if you will ever encounter speed issues you might think of optimizing
 
Jul 5, 2011 at 11:56 PM

Hi, I have a feeling that with the HyperDescriptor that this may fail in medium trust as it uses Reflection.Emit to do its work.

In other new however, we've created an implementation of ValueInjector that mimics most of the main Fluent aspects of AutoMapper so you can do things like:

 

this.CreateMap<AttributeDefinition, HiveIndexOperation>(true)
    .CreateUsing(() => new HiveIndexOperation())
    .ForMember(x => x.Entity, opt => opt.MapFrom(y => y))
    .ForMember(x => x.Operation, opt => opt.MapFrom(y => IndexOperationType.Add))
    .ForMember(x => x.Item, opt => opt.MapFrom(Map<AttributeDefinition, IndexItem>))
    .AfterMap((s, t) =>
        {
            t.Item.Fields.Add("CustomValue", "CustomProperty");
        });

 

I ran some benchmark tests with this vs Automapper and where ValueInjector really fell over was using the Convention based injector as it appears to iterate over every single combination of property matches. What also made it quite slow was the chaining of injectors together in your AutoMapper simulation. the benchmarks were originally at something like 10,000 iterations: ValueInjector = 10 seconds, Automapper = 300 ms. I created a custom injector called RulesBaseInjector which adheres to the rules you can see above but also does all of the functions listed in your injector chain (i.e. Nullables, Enums, etc....). The other thing I did to speed things up in this injector was to track which properties had already been set by using a new injector context object so that when it calls the mapper for a child operation because it can't figure it out or if another injector is in the chain, it does try to re-set the property, it just ignores and continues.

Now, the benchmarks show this: 10,000 iterations: ValueInjector: 600ms, Automapper: 300ms. Which is MUCH better :) Automapper uses dynamic proxy and other things like how the HyperDescriptor works which makes it marginally faster but makes it unusable in medium trust.

Coordinator
Jul 6, 2011 at 7:07 AM
Edited Jul 6, 2011 at 7:11 AM

hi, good job

my idea in the automapper simulation  is that instead of doing stuff like this.CreateMap<a,b,>().ForMember(x1 ... ).ForMember(x2 ...) etc.

you would create a class TypeMapper<T1,T2>

where you could use injections and plain c# code, so this way you can do anything you want (full flexibility)

but that's only for the types <T1,T2> that you want to specify something custom for the rest a generic TypeMapper<,> is used 

and you can actually inherit this generic TypeMapper<,> when creating custom ones, or you might not

-----

so with automapper approach you could write something like 

ForMember(x => x.Prop1, y = y.Prop7)

with my approach simply x.Prop1 = y.Prop7;

so there's no need to learn new APIs or ask for new features

Jul 6, 2011 at 7:13 AM

Yup, so we've allowed for this as well, the CreateMap<T1, T2> is just an extension method that creates an InjectorMapper for you, but the CreateMap method has many overrides and also excepts any IInjectorMapper that you specify so it will do both... use your custom mapper and also adhere to the 'rules' like ForMember, AfterMap, etc...

The other thing we implemented was the ability to allow for inheritance during mapping which is just another overload for CreateMap. The engine then stores a ConcurrentBag<Lazy<IInjectorMapper, TypeMapperMetadata>> of which the meta data stores information about things like if inheritance is supported.

Anyways, its all working quite well! :)

Coordinator
Jul 6, 2011 at 7:19 AM
Edited Jul 6, 2011 at 7:20 AM

about chaining the injections, it's true each injection can have it's very own algorithm of traversing the properties

and the Convention is traversing all of them

the thing is that when you apply one or more injection usually you want to do something like a.Prop1 = b.Prop1

but you also have the possibility to do stuff like a.Prop1 =b.Prop1 , /* next injection * / a.Prop1 += c.Prop1 /* next injection*/ a.Prop1 -= d.Prop1 * d.Prop4

so each injection has it's own algorithm and has access to all of the properties, but of course you can limit this if you need to like you did

one simple way would be to merge the Convention injections that have the same rules for the Match method

and in the SetValue method you can check for the types and use a different "setting value algorithm"

Coordinator
Jul 6, 2011 at 7:26 AM

about the inheritance usually my generic TypeMapper handles all the base and other classes because of the way the Injections work

but if I do need to add  something very custom to a TypeMapper that already does 70% of the work than,

 I just inherit it and ovveride the virtual Map method, specify my stuff and call the base.Map

Jul 6, 2011 at 7:48 AM

yup, that's why i'm liking this lib a lot, its very flexible!

Coordinator
Jul 7, 2011 at 8:12 AM
Edited Jul 7, 2011 at 8:13 AM

btw, I tried your code, if you put 1 or 5  iterations (which I need most of the time) instead of 10000 you get faster results for VI 

Coordinator
Jul 8, 2011 at 8:26 AM

Hi,

I did a new type of injection that I think will solve the performance issue, this one first time learns how to map the types that you gave to it and after it will do it faster

I called it the SmartConventionInjection

you can see it here: http://valueinjecter.codeplex.com/wikipage?title=SmartConventionInjection

I would like to know your opinion about it

Thanks!

Jul 11, 2011 at 8:28 AM

nice.

We've simply stuck to having our injector do the name matching (just like your default injector) which is obviously very fast and doesn't iterate over each property pair and for anything custom for specific members we're just using our 'rules' like

ForMember(x => x.MyProperty, opt => opt.MapFrom(y => y.Value + 123)

or to ignore:

ForMember(x => x.MyProperty, opt => opt.Ignore())

Obviously this is just like Automapper but we've just taken this approach so that we can inline do anything custom to a particular member that we like without having to write a custom injector, but obviously if there is more complex work, we just have a custom injector. Your SmartConventionInjector will be much better for anyone that will use the convention injector to 'Match' properties though, well done!

Another reason we've gone down the route of making this work similarly to Automapper is because we had already written a ton of mappings in our codebase hoping that Automapper v2 would support Medium trust which they have said it would, but we've tested the alpha and it doesn't so we've had to look for alternatives... which is now ValueInjector. Now that we've written our own injector to support the Automapper style syntax, we can fairly easily swap out Automapper without changing too much of our already existing code. 

If you want to see our implementation, you can have a look at the Umbraco v5 source on the 'default' branch ( http://umbraco.codeplex.com ). We've created a seperate proj for the mapping engine called:  Umbraco.Framework.TypeMapping

Cheers!

Coordinator
Jul 11, 2011 at 8:33 AM

Yes,  I've been looking through the code of Umbraco.Framework.TypeMapping :), Very Nice 

keep it up !!!

Jul 11, 2011 at 9:05 AM

That lib is basically the ValueInjector engine, but if you have a look at the TypeMapperCollection on the IFrameworkContext, the basic idea behind everything is that if one type needs to go to another it just need to be added to a mapper which the collection refers to. Then we have things like:

MapToIntent<T>(obj)

which basically does something like:  I have an object but i don't know what type it is, and don't really care, all I know is I need to go to type 'T'

then so long as a mapper is registered to go from the current object to 'T' then it will 'just work'. In about a months time, we plan on implementing that further to have a shortest path algorithm so say you have a mapping from type A -> B and another from type B -> C then if I do this:

var c = MapToIntent<C>(myAObject);

it will just figure out how to get from A -> C

which will be sweeet :)

Coordinator
Jul 11, 2011 at 11:31 AM
Edited Jul 11, 2011 at 11:32 AM

well it looks like you will need an algorithm something like on linkedin where you visit someone's profile and it tells you that you are connected with this person through that and that person.

a problem could be that you might have many paths to the same target, or probably you could just pick the first one

also if we look at this in a very simple way if we would have the types: foo bar and foobar ( foobar has the union of properties of foo and bar )

and we would do:

from foobar to bar

via foobar->foo -> bar

foo.injectFrom(foobar) foo gets values of his props from foobar

bar.injectFrom(foo) bar gets nothing

while if we would do:

bar.injectFrom(foobar) bar gets stuff

of course your implementation could be very different than my current thoughts about this

I prefer to stick with simpler ways of doing mapping, mainly I just don't consider types at all, I just try to think of all Types as anonymous and map the properties via conventions
you can look at http://prodinner.codeplex.com in this project I show how I use the ValueInjecter, it's a quite simple mapper, but it's all I need

( I use it to map from entities to viewmodels and back, everything is done by one single class with some injections)

http://code.google.com/p/prodinner/source/browse/trunk/WebUI/Mappers/Mapper.cs // the generic one

http://code.google.com/p/prodinner/source/browse/trunk/WebUI/Mappers/DinnerMapper.cs // one with some custom stuff