How to inject value with no source property defined

Nov 1, 2011 at 4:14 AM

Hi, I have question about how to inject value with no source property defined. so I have 2 class :
User.cs
    

public class User
{
    public int UserId { get; set; }
    public string UserName { get; set; }
}

and UserViewModel.cs
   
public class UserViewModel
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string RoleName { get; set; }
}

how to inject the RoleName? I need to call my service to check to database what user's role. and I dont wanna the service called in get property like this :
public string RoleName
{
        get { return Roles.GetRolesForUser(UserName).FirstOrDefault(); }
}


I want to achieve this using value injecter.

I have tried with implement ConventionInjection abstract class but still confused how to match the object.

Thank you in advance

Coordinator
Nov 1, 2011 at 7:04 AM

why not do this:

uvm.injectFrom(u);

uvm.RoleName = Roles.GetRolesForUser(u.UserName).FirstOrDefault()

Nov 1, 2011 at 7:25 AM
Edited Nov 1, 2011 at 7:43 AM

Hi, thanks for response

If I wanna put it in many places then it will be repeating code and I just curious how to do this in value injecter, I usually use CustomResolver in automapper.

any other way?

Coordinator
Nov 1, 2011 at 7:59 AM

create a class that will wrap all this and you don't have put this in many places

you can also use this approach: http://valueinjecter.codeplex.com/wikipage?title=Automapper%20Simulation&referringTitle=Home it looks complicated because it also maps collections

a simpler approach here: http://prodinner.codeplex.com there's also a pdf where IMapper is explained a bit

Nov 2, 2011 at 6:48 AM

okay solved..

thanks man,,

we can implement NoSourceValueInjection abstract class..here is the code

public class SetRole : NoSourceValueInjection
     {
         protected override void Inject(object target)
         {
             dynamic t = target;
             t.RoleName = Roles.GetRolesForUser(t.UserName).FirstOrDefault();
         }
     }

uvm.InjectFrom(user)
   .InjectFrom<SetRole>();

Aug 27, 2012 at 8:48 PM
o wrote:

create a class that will wrap all this and you don't have put this in many places

you can also use this approach: http://valueinjecter.codeplex.com/wikipage?title=Automapper%20Simulation&referringTitle=Home it looks complicated because it also maps collections

a simpler approach here: http://prodinner.codeplex.com there's also a pdf where IMapper is explained a bit

I want to use the IMapper for custom logic mapping like mapping to a different property name/type etc.

but I also want to use the convention based mapping with IValueInjecter. Therefore I have to injected sometimes 2 different interfaces into my asp.net mvc controllers.

Is it possible somehow to use only one interface and have the benefit of convention AND custom logic based mapping?

Coordinator
Aug 27, 2012 at 9:01 PM

yes, that's what the IMapper is for in prodinner

if you look at the DinnerMapper it inherits the generic Mapper which has convention stuff in it (injection calls) and some custom mapping 

    public class DinnerMapper : Mapper<Dinner, DinnerInput>
    {
        public override Dinner MapToEntity(DinnerInput input, Dinner e)
        {
            var entity = base.MapToEntity(input, e);

            entity.Start = entity.Start.AddHours(input.Hour).AddMinutes(input.Minute);
            entity.End = entity.Start.AddMinutes(input.Duration);
            
            return entity;
        }

        public override DinnerInput MapToInput(Dinner entity)
        {
            var input = base.MapToInput(entity);

            input.Minute = entity.Start.Minute;
            input.Hour = entity.Start.Hour;
            input.Duration = (int)entity.End.Subtract(entity.Start).TotalMinutes;

            return input;
        }
    }

public class Mapper<TEntity, TInput> : IMapper<TEntity, TInput>
        where TEntity : class, new()
        where TInput : new()
    {
        public virtual TInput MapToInput(TEntity entity)
        {
            var input = new TInput();
            input.InjectFrom(entity)
                .InjectFrom<NormalToNullables>(entity)
                .InjectFrom<EntitiesToInts>(entity);
            return input;
        }

        public virtual TEntity MapToEntity(TInput input, TEntity e)
        {
            e.InjectFrom(input)
               .InjectFrom<IntsToEntities>(input)
               .InjectFrom<NullablesToNormal>(input);
            return e;
        }
    }
hth

Aug 27, 2012 at 9:11 PM

 

1.) Generic base class for convention mapping

2.) Inherited class overriding convention mapping with custom mapping

3.) All I need is the IMapper, but no IValueInjecter

 

Are these assumptions correct?

Coordinator
Aug 27, 2012 at 9:31 PM

yes, correct

and you can do the generic mapping anyway you want, so using ValueInjecter is not required

Aug 27, 2012 at 9:37 PM
Edited Aug 27, 2012 at 9:43 PM

one last no two question before I am glad to throw away AutoMapper and use your tool :P

1.) Why is the IMapper interface and generic class not part of your ValueInjecter assembly?

2.) As the IMapper is a generic interface, how can I pass it to my CustomerController without defining the concrete types when instantiating a controller?

Answer: Not possible! Why do I ask this? Because I could want to map from a SpecialCustomer to a CustomerCreateViewModel or from a Customer to a CustomerCreateViewModel.

Hm... this would mean I have to inject the same interface 2 times but with different generic types, right?

Do you have a good solution for such a case?

Coordinator
Aug 27, 2012 at 10:52 PM
Edited Aug 27, 2012 at 10:54 PM

IMapper can be used without ValueInjecter

your IoC will instantiate types and pass it to the controller

in prodinner in WindsorConfigurator.cs you can see this:

 

 

WindsorRegistrar.Register(typeof(IMapper<Dinner,DinnerInput>), typeof(DinnerMapper)); // this registers the concrete DinnerMapper
    
    ...        
WindsorRegistrar.RegisterAllFromAssemblies("Omu.ProDinner.WebUI"); // this registers the generic mapper

so when Winsor sees IMapper<Dinner,DInnerInput> it will use DinnerMapper in rest it will use the Generic one

Aug 28, 2012 at 8:20 PM

yes I know about Ninject and IOC that I use. Thats not the problem. I guess I expressed myself not correctly.

 

I want to map a Model to a ViewModel. Depending on the action like Create,Delete,Update,Get I have a different ViewModel.

 

That means I want to map from a

customerList to a OpenCustomerListViewModel,

customer to a CreateCustomerViewModel

etc...

But with IMapper I can only have one mapping in a direction customerEntity to customerViewModel. 

Maybe there is no problem, do you see a problem in my context/case?

Coordinator
Aug 28, 2012 at 9:11 PM

IMapper<T1,T2>

so you can have IMapper<Model, VM>

and IMapper<VM, Model>

Aug 29, 2012 at 6:52 PM
Edited Aug 29, 2012 at 7:37 PM

an Example:

When I have to map a Dinner to a DinnerViewModel and the other way round this constructor parameter seem fine.

public DinnerController(ICrudService<Dinner> s, IMapper<Dinner, DinnerInput> v) : base(s, v)
{
}
but if I have different mvc actions where I need to show different viewModel from one entity type then I have
to make a constructor like this:

public DinnerController(ICrudService<Dinner> s, IMapper<Dinner, OpenDinnerViewModel> mapper1,IMapper<Dinner,DeleteDinnerViewModel> mapper2,etc...) 
{
}

What if I want to inject into the constructor this:

IMapper<Dinner,OpenDinnerViewModel>
IMapper<Dinner,EditDinnerViewModel>
IMapper<Dinner,CreateDinnerViewModel>
IMapper<Dinner,DeleteDinnerViewModel>

then I have to pass 4 Imapper instances.

a new controller with all that parameters is stupid.

With AutoMapper I have IMapperEngine and not more!

You have something similar I could use?

Coordinator
Aug 29, 2012 at 7:03 PM

I'm sorry but, I don't understand your question

Aug 29, 2012 at 7:38 PM

I edited my former post please check :)

Coordinator
Aug 29, 2012 at 8:17 PM

ok,, so the current version of prodinner has this interface:

public interface IMapper<TEntity, TInput> where TEntity : class, new() where TInput : new()
    {
        TInput MapToInput(TEntity entity);
        TEntity MapToEntity(TInput input, TEntity entity);
    }

which requires to specify mapping forward and backward

if you need more than one mapper than yes you have to resolve them somehow (pass in constructor or IoC.Resolve)

usually the Generic one handles everything so there's no need for custom ones

Aug 29, 2012 at 9:26 PM

ok I just found this in the prodinner solution:

 

      
  public UserController(IMapper<User, UserCreateInput> v, IMapper<User, UserEditInput> ve, IUserService s) : base(s, v, ve)
        {
            this.s = s;
        }

2 x IMapper with Create and Edit hehe ok then I have to do it that way...

Thanks a bunch for your strong help!