Inject into ViewModel without ExportService attribute

Jan 28, 2011 at 3:25 PM

Hi,

What if you have something in a separate assembly that you don't want to have a dependency on MefedMVVM?

Is there any way to inject something into a ViewModel without marking it with the ExportService attribute?

 

cheers,

Jordan.

Coordinator
Jan 31, 2011 at 8:35 AM

No I am afraid not, as the ViewModel is created by MeffedMVVM. What you could do though is have a seperate non MeffedMVVM service repository and get those services in to your ViewModel using your own custom code. But as for constructor parameters, these MUST all come via MeffedMVVM DPs on View.

 

Sorry that is the just the way it is. Nothing stopping you doing the non MeffedMVVM service repository, and just resolve those in your ViewModel constructor code though. I can not tell you what this will look like as that is essentially your call.

 

I obviously suggest you take the dependency on MeffedMVVM.

Jan 31, 2011 at 9:45 AM

Hi,

Thanks for the reply.

Often you might want to inject something from the business service layer into the ViewModel, so it wouldn't be right to create a dependency on MeffedMVVM in that.

I'm just looking at possible frameworks at the moment so this isn't a massive problem for me.

 

Jordan

Coordinator
Jan 31, 2011 at 11:06 AM

Fair enough, for me the services would be the things that call the business layer, and I would want those services in my ViewModel. But whatever works for you really. Like I say you could do it using external services and resolve these in ViewModel constructor.

Jan 31, 2011 at 1:27 PM

Yes - good point, I could use some sort of service locator / DI container and resolve those in the constructor of the ViewModel.

Just out of interest, how do you inject your business layer into those services then?

 

Coordinator
Jan 31, 2011 at 3:47 PM
Edited Jan 31, 2011 at 3:52 PM

Well my services that the ViewModel uses would typically be used to retrieve stuff from the data layer, and return a List of something, so say my ViewModel wanted to show a list of users that had a certain role, I would have a PeopleService (meffed up with attributes as shown in Cinch articles), and that UserService would talk to whatever is responsible for supplying that data, via dedicated methods, so for this example it would have method like

 

public interface IPeopleService

{
   List<User> GetUserWithPrivelege(string privName)
}


Public class ActualPeopleService : IPeopleService
{
    public List<User> GetUserWithPrivelege(string privName)
    {
	//Call whatever gets the data, for me typically
        //WCF call here
    }
}

 

Typically for me, this would be to use a WCF proxy to hit the WCF service and return me a list of people. So my ViewModel knows about the service through a INterface driven service (so it can be mocked), and that service calls WCF to get some data, and that WCF service returns the values to the WPF service in the ViewModel, and the WPF service in the ViewModel simply returns the data to the ViewModel.

It is a pretty common setup so I am surprised you find this does not work for you.

In the WPF demo code that goes with Cinch this can be done Synchronously or Async using callback Func<T,TResult>, see the demos for that.

Jan 31, 2011 at 3:58 PM
Edited Jan 31, 2011 at 4:02 PM
sachabarber wrote:

public interface IPeopleService

{
   List<User> GetUserWithPrivelege(string privName)
}


Public class ActualPeopleService : IPeopleService
{
    public List<User> GetUserWithPrivelege(string privName)
    {
	//Call whatever gets the data, for me typically
        //WCF call here
    }
}

The reason this does not normally work for me is that whatever is being called to get the data would also be an interface, and I would have injected an implementation of that into the ActualPeopleService.

I use dependency injection all the way through the stack - so I inject the data access layer into the business layer, then inject the business layer into the next layer up, etc.

 

However, in my current project there may be WCF calls as you have shown in your example. So I could just wrap them up just as you have shown here and it would work.

Coordinator
Jan 31, 2011 at 4:43 PM

We do exactly that too at work (ok this example is not based on Cinch, but you could see how this could be put into a CInch PeopleService say). So you could have something that is resolved through DI inside of the PeopleService, so we typically have something like this so we can either use the real WCF one (ActualWCFService which is assumed if no castle (IOC Container) setting is found to override it)

 

 

using System.Collections.Generic;
using System.Linq;
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
using Castle.Core.Resource;

namespace WCF.Proxy
{
    public interface IWCFService
    {
        IEnumerable<User> GetusersWithPriv(String priv);
    }

    public class ActualWCFService: IWCFService
    {
        public IEnumerable<User> GetusersWithPriv(String priv)
        {
            return Service.GetusersWithPriv(priv);
        }
    }



    public class ServiceCaller
    {
        private static IWCFService service;

        static ServiceCaller()
        {
            try
            {
                IWindsorContainer container = new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));
                service = (IWCFService)container.Resolve(typeof(IWCFService));
            }
            catch
            {
                if (service == null)
                    service = new ActualGateway();
            }
        }



        public static IEnumerable<User> GetusersWithPriv(String priv)
        {
            return service.GetusersWithPriv(priv)
        }
    }
}



Where the ActualWCFService makes use of this WCF helper class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using System.ServiceModel;
using System.Threading;
using Moneycorp.Common.Logging;
using Moneycorp.Common.Security;
using Moneycorp.Gateway;
using Moneycorp.Omni.UI.ServiceLayer;

namespace Moneycorp.UI.ServiceLayer
{
    public static class Service
    {
        private static IClientChannel proxy = null;
        private static ChannelFactory<ISomeService> _channelFactory = null;

        static Service()
        {
            try
            {
                String defaultBinding = ServiceSettingsConfigurationHelper.Instance.DefaultBindingName;
                _channelFactory = new ChannelFactory<ISomeService>(defaultBinding);
            }
            catch (Exception e)
            {
                ApplicationException ae = new ApplicationException("Error initiating WCF channel", e);
                LogManager.Log(LogLevel.Error, ae);
                throw ae;
            }
        }



        public static IEnumerable<User> GetusersWithPriv(String priv)
        {
            try
            {
                if (proxy == null || (proxy.State == CommunicationState.Closed
                                    || proxy.State == CommunicationState.Faulted
                                    || proxy.State == CommunicationState.Closing))
                {
                    proxy = (IClientChannel)_channelFactory.CreateChannel();
                    proxy.Open();

                return ((ISomeService)proxy).GetusersWithPriv(priv)
            }
            catch (FaultException<ArgumentOutOfRangeFault> e)
            {
                LogManager.Log(LogLevel.Error, e);
            }
            //Many more catches
            //Many more catches
            //Many more catches
            //Many more catches
            //Many more catches
            //Many more catches
        
    }
}

Like I say this is not based on Cinch, but it would be easy enough to adapt

So inside your WPF peopleService method you could simply use ServiceCaller.GetusersWithPriv(priv) which would either use the WCF real one, or one you fed in using some sort of IOC Container (castle in this example) something like this

 

public interface IPeopleService

{
   List<User> GetUserWithPrivelege(string privName)
}


Public class ActualPeopleService : IPeopleService
{
    public List<User> GetUserWithPrivelege(string privName)
    {
	return ServiceCaller.GetUserWithPrivelege(privName)
    }
}

 

Feb 2, 2011 at 10:36 AM

Cool, cheers. I suppose its definitely good practice to wrap any web service calls up behind an interface anyway.

I still think that its a limitation of MeffedMVVM that you are forced to wrap something up like that, but of course, not a show stopper.

Coordinator
Feb 2, 2011 at 1:47 PM

I don't see it that way, as if you want to mock or use test double you would always want to abstract that behind interface anyway.

Feb 2, 2011 at 2:46 PM
Edited Feb 2, 2011 at 2:49 PM

But what if it is *already* wrapped up behind an interface.

 

Imagine an architecture where you are using an ORM like nhibernate, and choosing not to convert your domain objects to DTOs when passing them from the service layer to the front end. (this has advantages and disadvantages, but it certainly an approach that many projects take).

So you would inject your data access layer into the service layer, and then the service layer into the front end. The ActualPeopleService from your example before would look like this:

 

 

public interface IPeopleService

{
   IList<User> GetUserWithPrivelege(string privName)
}


public class ActualPeopleService : IPeopleService
{
    private IUserDao userDao;

    public ActualPeopleService(IUserDao userDao)
    {
         this.userDao = userDao;
     } 

    public IList<User> GetUserWithPrivelege(string privName)
    {
	  return userDao.GetUserWithPrivelege(privname);
    }
}



I've worked on projects like this where the services are reused in overnight batch processes.
The service implementations are in a separate assembly from the WPF stuff, and it wouldn't be right for them to have a dependency on MeffedMVVM.
I'm being extra fussy of course - because it wouldn't be the end of the world to introduce that dependency.

regards,
Jordan.

 

 

Coordinator
Feb 2, 2011 at 3:53 PM

Jordan

 

I do you see your point, but I think it could be worked out somehow. Anyway sounds like we will have to agree to disagree (at least a bit). Still been fun talking to you

Feb 2, 2011 at 4:03 PM

Hehe, yes I'll agree to disagree (at least a bit).

Anyway, I'm certainly not really in a position to be criticising someones framework when I haven't got my own :)

Have a good day,

Jordan.

Coordinator
Feb 2, 2011 at 4:11 PM

No problem its all good stuff.

Sep 25, 2011 at 5:35 PM
gusgorman wrote:

Hi,

What if you have something in a separate assembly that you don't want to have a dependency on MefedMVVM?

Is there any way to inject something into a ViewModel without marking it with the ExportService attribute?

 

cheers,

Jordan.

Then mark it with [Export(typeof(IMyService))] this dependency is on .NET

Coordinator
Sep 26, 2011 at 10:01 AM

Yes you could do that but I do not know whether MeffedMVVM will honor that as a ImportingConstructor as I believe MeffedMVVM uses a specialized catalog. May work, may not have not tried it. Its worth a shot though