0

I developed a MVC application using abp boilerplate and now I have the necessity to expose some services via WFC/SOAP.

The idea is to create a WFC Service, inject the required IApplicationService and use it.

Something like:

// this code does not work
public class MyFirstService : IMyFirstService, ITransientDependency {
    private readonly ICourseAppService _courseAppService;

    // Injection here does not work!
    public MyFirstService(ICourseAppService courseAppService) {
        _courseAppService = courseAppService;
    }

    public CourseDto GetData(int id) {
        return _courseAppService.Get(id);
    }
}

But this code does not work. :-(

The first error I have is from WCF saying the Service does not have a default constructor without parameters. So I am on the wrong way.

How can I inject the service into the SOAP service?

The answer https://stackoverflow.com/a/46048289/752004 did not help me.

Gianpiero
  • 3,349
  • 1
  • 29
  • 42

2 Answers2

0

WCF uses Reflection to create service instance, so if your service has no constructor without parameters , wcf will fail to create the service instance which is why wcf shows the error.

It is not easy to integrate injection framework with wcf.

You should customize instance provider(which provides wcf service instance).

https://blogs.msdn.microsoft.com/carlosfigueira/2011/05/31/wcf-extensibility-iinstanceprovider/

In your customized instance provider , you could provide your injected service instance in the method GetInstance.

Then you should make wcf use your own instance provider using service behavior.

For example

 public class MyServiceAttribute : Attribute, IServiceBehavior
{
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {

    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher item in serviceHostBase.ChannelDispatchers)
        {
            foreach (EndpointDispatcher item1 in item.Endpoints)
            {
                item1.DispatchRuntime.InstanceProvider = new MyInstanceProvider(); // apply customized instanceProvider
            }
        }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {

    }
}

Then you should customize a ServiceHost to apply the service behavior. Like

 public class MyUnityServiceHost : ServiceHost
{

    protected MyUnityServiceHost()
    {
    }

    protected override void OnOpening()
    {
        base.OnOpening();
        if (this.Description.Behaviors.Find<MyServiceAttribute >() == null)
        {
            this.Description.Behaviors.Add(new MyServiceAttribute ());//add your behavior
        }
    }
}

At last, you should customize HostFactory to create your customized servicehost. https://blogs.msdn.microsoft.com/carlosfigueira/2011/06/13/wcf-extensibility-servicehostfactory/

You could refer to the similar discussion below.

Injecting data to a WCF service

Ackelry Xu
  • 756
  • 3
  • 6
  • Thank you for your answer. It was not the solution when using the abp boilerplate framework, but it put me on the right way. – Gianpiero Jan 05 '19 at 18:46
0

Abp uses the Castle Windsor, so following the suggestions from this answer and this article I found the solution.

  1. Once imported the nuget package Castle.WcfIntegrationFacility, I created a new WCF library and in it I created a AbbModule class where I registered MyService (defined in pt. 3):
[DependsOn(typeof(BookingCoreModule), typeof(BookingApplicationModule))]
public class BookingSoapModule : AbpModule {

    public override void Initialize() {
        IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());

        IocManager.IocContainer.AddFacility<WcfFacility>().Register(
            Component
                .For<IMyService>()
                  .ImplementedBy<MyService>()
                  .Named("MyService")
        );
    }
}
  1. Then I created my IMyService interface (note that it extends ITransientDependency):
[ServiceContract]
public interface IMyService : ITransientDependency {
    [OperationContract]
    CourseDto GetCourse(int courseId);
}
  1. Finally I implemented the interface with a constructor using injection:
public class MyService : IMySecondService {

    private readonly ICourseAppService _courseAppService;
    public IAbpSession AbpSession { get; set; }
    public ILogger Logger { get; set; }

    public MyService(ICourseAppService courseAppService) {
        AbpSession = NullAbpSession.Instance;
        Logger = NullLogger.Instance;

        _courseAppService = courseAppService;
    }

    public CourseDto GetCourse(int courseId) {
        AsyncHelper.RunSync(async () => {
            var course = await _courseAppService.Get(courseId);
            return course;
        });
    }

}
Gianpiero
  • 3,349
  • 1
  • 29
  • 42