简体   繁体   中英

Generic ServiceFactory<T> for WCF Channel Factory and StructureMap with MVC 3

So this will be an interesting post because I must include all my code and will attempt to explain clearly how I have setup my architecture.

I have placed all my Service and DataContracts in a central assembly (DMT.WCF.Contracts). This is done so that the distributed pieces of my application can all reference the same type of service interfaces and contracts which is very nice.

I have setup a StructureMap container to inject my dependencies in the following manner, by specifying a ServiceContext, which will house all of the Service Interface properties so that they can be referenced int he application later.

public interface IServiceContext
{

}

public class ServiceContext: IServiceContext
{
    public IAuthenticationService AuthenticationService { get; set; }
    public ServiceContext(IAuthenticationService authenticationService)
    {
         AuthenticationService = authenticationService;
    }
}

Then, I have my StructureMapControllerFactory which looks like the following:

public class StructureMapControllerFactory:DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null) return null;
        return ObjectFactory.GetInstance(controllerType) as IController;
    }
}

and this is configured in my global.asax like the following:

protected void Application_Start()
    {
        ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
        AreaRegistration.RegisterAllAreas();
        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        Configure();

    }

I wanted to decouple my services as much as possible from my appliction, so I have implemented the following ServiceFactory class that handles providing proxies to StructureMap when the IoC container is configured:

public  static class ServiceFactory
{
    private static readonly ClientSection _clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;

    public static T Create<T>()
    {
        T context = default(T);
        foreach(ChannelEndpointElement endpoint in _clientSection.Endpoints)
        {
            if(endpoint.Contract == typeof(T).FullName)
            {
                IEnumerable<Type> assignables = typeof (Binding).Assembly.GetTypes().Where(p => typeof(Binding).IsAssignableFrom(p));
                Type bindingType = assignables.Single(p => p.Name.ToLower().Equals(endpoint.Binding.ToLower()));
                context = ChannelFactory<T>.CreateChannel((Binding)Activator.CreateInstance(bindingType, false), new EndpointAddress(endpoint.Address));
            }
        }
        return context;
    }

}

This allows me to pull directly from the config file when creating proxies so I do not need to select "Add Service Reference" (as that is technically adding a dependency).

In my global.asax, I can now configure my StructureMap Container like this:

protected void Configure()
    {
        ObjectFactory.Configure(x =>
        {
            x.Scan(scanner => scanner.AddAllTypesOf<IController>());
            x.For<IAuthenticationService>().Use(ServiceFactory.Create<IAuthenticationService>());
            x.For<IServiceContext>().Use<ServiceContext>();

        });
    }

Although I was initially able to use this in the following manner:

IAuthenticationService service = ServiceContext.AuthenticationService.Authenticat(...);

I am now unable to start my application without exceptions being thrown such as the following:

StructureMap configuration failures:
Error:  104
Source:  Registry:  StructureMap.Configuration.DSL.Registry, StructureMap,  Version=2.6.1.0, Culture=neutral, PublicKeyToken=e60ad81abae3c223
Type Instance '685e2e2a-f271-4163-a6fa-ba074e4082d1' (Object:   DMT.WCF.Contracts.Authentication.IAuthenticationService) cannot be plugged into type   DMT.WCF.Contracts.Authentication.IAuthenticationService, DMT.WCF.Contracts,  Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

I am not sure why this is occuring. Like I said, I was initially able to get this up and running, but am not sure what has changed.

I have looked at the many of hundreds of references regarding this error message, but they are all specific to problems that dont seem to match mine, unless I am overlooking my problem.

HELP!!!

Doesn't this operation use the ChannelFactory to new up a channel safe client?

context = ChannelFactory<T>.CreateChannel(
    (Binding)Activator.CreateInstance(bindingType, false),
    new EndpointAddress(endpoint.Address));

Well, two issues here. As Sixto Saez mentioned, there's WCF issues to consider. On the StructureMap front, my guess is that your factory method may be returning a default instance for an interface.

Two suggestions...

  1. Right after your container configuration, add a call to ObjectFactory.AssertConfigurationIsValid()...make sure you remove it again after you figure out what's wrong :-) it should throw a very similar error, but it will actually try to resolve every instance of every configured type. Usually you'll get a very verbose error with everything that's wrong. You can then start finding where your configuration error is.

  2. It may have something to do with your factory method for the pluggable type. You might try having those instances created on the fly. Use the IContext syntax to do that - context => // Make Foo Here.

protected void Configure()
{
    ObjectFactory.Configure(x =>
    {
        x.Scan(scanner => scanner.AddAllTypesOf<IController>());

        // Skip using this
        // x.For<IAuthenticationService>()
        //    .Use(ServiceFactory.Create<IAuthenticationService>());

        // Use the IContext syntax instead. Normally you'd grab the instance out of the
        // container, but you can use this to resolve an instance "live" from
        // somewhere other than the container
        x.For<IAuthenticationService>()
            .Use(context => ServiceFactory.Create<IAuthenticationService>());

        x.For<IServiceContext>().Use<ServiceContext>();
    });

    // Remove this from production code because it resolves the entire container...
    ObjectFactory.AssertConfigurationIsValid();
}

I'm guessing that using the IContext syntax may help fix the configuration errors. You can use the Assert to go from there if not. I think the other comments cover the WCF issues, but it's kind of hard to assess those while StructureMap is misconfigured.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM