开发者

WCF contract mismatch error using Autofac to register endpoint via ChannelFactory

开发者 https://www.devze.com 2023-02-27 10:20 出处:网络
I have a WCF service that works when accessed by a simple MVC application. When I try to make call on the same endpoint from a different MVC app that\'s wired up with Autofac I get a binding/开发者_J

I have a WCF service that works when accessed by a simple MVC application.

When I try to make call on the same endpoint from a different MVC app that's wired up with Autofac I get a binding/开发者_JAVA技巧contract mismatch exception like this:

Content Type application/soap+xml; charset=utf-8 was not supported by service http://localhost:6985/ProductService.svc. The client and service bindings may be mismatched.

System.Net.WebException: The remote server returned an error: (415) Unsupported Media Type.

I'm reasonably confident I do not have a mismatch in the configuration settings on either end, I base this confidence on testing the exact same settings on a WCF + MVC combination where Autofac is not present. The config settings are on pastebin.com/t7wfR77h.

I therefore would like some help analysing if the way I have registered the dependency/endpoint with Autofac is the issue...

*Application_Start* code in MVC app for Autofac setup:

var builder = new ContainerBuilder();
//other registrations...

builder.Register(c => 
            new ChannelFactory<IProductService>(
                new WSHttpBinding("ProductService_wsHttpBinding"),
                new EndpointAddress("http://localhost:6985/ProductService.svc")
            )
        ).SingleInstance();

builder.Register(c =>
        {
            var factory = c.Resolve<ChannelFactory<IProductService>>();
            return factory.CreateChannel();
        }
      ).InstancePerHttpRequest();

var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

(For completeness) where I make use of this is in a ProductController that has only 1 dependency to be injected, very simple:

public class ProductController : AsyncController
{
    private IProductService _service;

    public ProductController(IProductService ps)
    {
        _service = ps;
    }

    //...
    //later simply call
    _service.SomeMethod();
}


As mentioned in the comment to @Nick Josevski, I was able to get something similar to work.

In my MVC3 application's Application_Start method, I have the following code:

protected void Application_Start()
{
    var builder = new ContainerBuilder();
    builder.Register(c => new ChannelFactory<ICartService>("CartService")).SingleInstance();
    builder.Register(c => c.Resolve<ChannelFactory<ICartService>>().CreateChannel()).InstancePerHttpRequest();
    var container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

    // other MVC startup activities, like registering areas and routes
}

These registrations gather the WCF configuration data from Web.config. I've also gotten registrations to work with endpoints defined in code. For completeness, here's some of the pertinent client-side Web.config entries:

<system.serviceModel>
  <bindings>
    <basicHttpBinding>
      <binding name="BasicHttpBinding" ... />
    </basicHttpBinding>
  </bindings>
  <client>
    <endpoint address="http://localhost:50930/Purchasing/CartService.svc"
        binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding"
        contract="CartService.ICartService" name="CartService" />
  </client>
</system.serviceModel>

Then, in my controller, I have code like the following:

using Autofac.Features.OwnedInstances;

public class BulkCartController : Controller
{
    private readonly Owned<ICartService> cartService_;

    public BulkCartController(Owned<ICartService> cartService)
    {
        cartService_ = cartService;
    }

    protected override void Dispose(bool disposing) // defined in Controller
    {
        cartService_.Dispose();
        base.Dispose(disposing);
    }

    //
    // GET: /BulkCart/Get/1
    public ActionResult Get(int id)
    {
        var model = new ShoppingCart { ShoppingCartId = id };
        using (var cartService = cartService_)
        {
            model.Items = cartService.Value.GetCartProductItems(id);
        }
        return View("Get", model);
    }
}

Unit testing looks like this:

using Autofac.Features.OwnedInstances;
using Autofac.Util;
using Moq;

[TestMethod]
public void Get_ReturnsItemsInTheGivenCart()
{
    var mock = new Mock<ICartService>(MockBehavior.Strict);
    mock.Setup(x => x.GetCartProductItems(2)).Returns(new CartProductItemViewObject[0]);
    var controller = new BulkCartController(new Owned<ICartService>(mock.Object, new Autofac.Util.Disposable())); 
    var result = controller.Get(2);

    Assert.IsInstanceOfType(result, typeof(ViewResult));
    var view = (ViewResult)result;
    Assert.AreEqual("Get", view.ViewName);

    Assert.IsInstanceOfType(view.ViewData.Model, typeof(ShoppingCart));
    var model = (ShoppingCart)view.ViewData.Model;
    Assert.AreEqual(2, model.ShoppingCartId);
    Assert.AreEqual(0, model.Items.Length);
}

I validate disposal with a unit test defined in an abstract controller test base class:

[TestClass]
public abstract class ControllerWithServiceTestBase<TController, TService>
    where TController : Controller
    where TService : class
{
    [TestMethod]
    public virtual void Dispose_DisposesTheService()
    {
        var disposable = new Mock<IDisposable>(MockBehavior.Strict);
        disposable.Setup(x => x.Dispose()).Verifiable();

        var controller = (TController) Activator.CreateInstance(typeof(TController), new Owned<TService>(null, disposable.Object));
        controller.Dispose();

        disposable.Verify();
    }
}

One thing I don't know yet is whether this use of Owned<T> and Dispose() gives me adequate disposal, or whether I'll need to use a LifetimeScope as per An Autofac Lifetime Primer.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号