How to invoke an IApplicationService into a WCF SOAP services in abp Boilerplate? - wcf

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.

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

Abp uses the Castle Windsor, so following the suggestions from this answer and this article I found the solution.
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")
);
}
}
Then I created my IMyService interface (note that it extends ITransientDependency):
[ServiceContract]
public interface IMyService : ITransientDependency {
[OperationContract]
CourseDto GetCourse(int courseId);
}
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;
});
}
}

Related

How to write an extension method that allows you to set options without creating the options instance

I really like the pattern where I can configure a service through an option class without having to create it, but I can't find an example of how to write an extension method that allows me to use that same pattern such as the one below that exists for registering a DbContext.
services.AddDbContext<MyDbContext>(options => options.EnableDetailedErrors());
I can see the method signature uses an action method, but I can't seem to find the extension class in GitHub for ASP.NET Core that shows me how to write an extension method using that type of option builder pattern.
For example, take the following service code. How would I write the extension method so that I could configure the options during service registration.
public void ConfigureServices(IServiceCollection services)
{
services.AddMyService(options => options.SomeSetting = true);
}
public interface IMyService
{
void DoSomething();
}
public class MyService : IMyService
{
private readonly MyServiceOptions _options;
public MyService(IOptions<MyServiceOptions> options)
{
_options = options.Value;
}
public void DoSomething()
{
Console.WriteLine(_options.SomeSetting);
}
}
public static class MyServiceExtensions
{
// How would I write this extension method so that I could configure it with options overload
public static IServiceCollection AddMyService(this IServiceCollection services, Action<MyServiceOptions> configure)
{
services.AddSingleton<IMyService, MyService>();
return services;
}
}
ASP.NET Core provides this mechanism with the IConfigureOptions
interface. You implement this interface in a configuration class and
use it to configure the IOptions object in any way you need.
It's as easy as:
public class MyServiceConfiguration : IConfigureOptions<MyServiceOptions>
{
private MyServiceOptions _options;
public MyServiceConfiguration(IOptions<MyServiceOptions> options)
{
_options = options.Value;
}
public void Configure(MyServiceOptions options)
{
options.SomeSetting = _options.SomeSetting;
options.SomeOtherSetting = _options.SomeOtherSetting;
}
}
All that remains is to register this implementation in the DI container.:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyServiceOptions>(options => options.SomeOtherSetting = true);
services.AddSingleton<IMyService, MyService>();
}
With this configuration, when IOptions is injected into your service, the MyServiceOptions object will be configured by the ConfigureMyServiceOptions class.
Be careful! The ConfigureMyServiceOptions object is registered as a singleton,
so it will capture any injected services of scoped or transient lifetimes.

Asp.Net Core No service for type has been registered

I have the following two classes
public class RepositoryConnection : IRepositoryConnection{
public RepositoryConnection(IConfiguration configuration, ILogger<RepositoryConnection> logger){
//STUFF
}
}
public class AuthenticationTokenFactory : IAuthenticationTokenFactory {
public AuthenticationTokenFactory(ILogger<AuthenticationTokenFactory> logger) {
//STUFF
}
}
Here is my Startup.cs
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services) {
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSingleton<IAuthenticationTokenFactory, AuthenticationTokenFactory>();
services.AddSingleton<IRepositoryConnection,RepositoryConnection>();
}
}
I can successfully inject IAuthenticationTokenFactory to controllers but when i try to inject IRepositoryConnection i get the following error→
InvalidOperationException: No service for type 'TrainingCommerce.Accessors.RepositoryConnection' has been registered.
Thanks to comments i immediately noticed my wrongful ways.
I was trying to access at another line
var debug = ControllerContext.HttpContext.RequestServices.GetRequiredService<RepositoryConnection>();
Try injecting the interface instead of the implementation:
In your sample you inject ILogger<RepositoryConnection> logger this is a typo and should be: ILogger<IRepositoryConnection> logger.
So:
public class RepositoryConnection : IRepositoryConnection{
public RepositoryConnection(IConfiguration configuration, ILogger<IRepositoryConnection> logger){
//STUFF
}
}

WCF with Sharp architecture - The needed dependency of type could not be located with the ServiceLocator

I'm working with an application which uses wcf and sharp architecture, I'm trying to create a service to write to the database. Here is my service: (Sicaf.Core.Services.Wcf)
[ServiceContract]
public interface IFacturaWcfService : ICloseableAndAbortable
{
[OperationContract]
string ConsultarValorMatricula(string xmlData);
}
[ServiceBehavior, AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class FacturaWcfService : IFacturaWcfService
{
private readonly IFacturaBusiness facturaBusiness;
public FacturaWcfService(IFacturaBusiness facturaBusiness)
{
this.facturaBusiness = facturaBusiness;
}
public string ConsultarValorMatricula()
{
return facturaBusiness.GetFactura();
}
public void Abort() { }
public void Close() { }
}
In the ComponentRegistrar.cs: (Sicaf.Core.Services.WebServices)
private static void AddWcfServicesTo(IWindsorContainer container)
{
// Since the TerritoriesService.svc must be associated with a concrete class,
// we must register the concrete implementation here as the service
container.AddComponent("facturaWcfService", typeof(FacturaWcfService));
}
I created a client but I get this exception:
The needed dependency of type FacturaWcfService could not be located with the ServiceLocator. You'll need to register it with the Common Service Locator (CSL) via your IoC's CSL adapter.
I've finally found my mistake.
Before:
private static void AddCustomRepositoriesTo(IWindsorContainer container)
{
// Register Data Layer Services
container.Register(
AllTypes.Pick()
.FromAssemblyNamed("Sicaf.Core.Services.Data")
.WithService.FirstNonGenericCoreInterface("Sicaf.Core.Services.Services.Data"));
}
After:
private static void AddCustomRepositoriesTo(IWindsorContainer container)
{
// Register Data Layer Services
container.Register(
AllTypes.Pick()
.FromAssemblyNamed("Sicaf.Core.Services.Data")
.WithService.FirstNonGenericCoreInterface("Sicaf.Core.Services.Services.Data"));
container.Register(
AllTypes.Pick()
.FromAssemblyNamed("SismatV2.Data")
.WithService.FirstNonGenericCoreInterface("SismatV2.Services.Data"));
}

Using Castle Windsor WCF facility with WCF 4 REST service gives 'Could not find a component' error

As a long time reader of StackOverflow but not finding the solution to my problem here is my first attempt to ask a question, so don't be too harsh on me :-)
I have the following WCF 4 REST service definitions:
Service contract
namespace RestService2.Service
{
[ServiceContract]
public interface ISampleService
{
[WebGet(UriTemplate = "")]
List<SampleItem> GetCollection();
[WebInvoke(UriTemplate = "", Method = "POST")]
SampleItem Create(SampleItem instance);
[WebGet(UriTemplate = "?id={id}")]
SampleItem Get(int id);
[WebInvoke(UriTemplate = "?id={id}", Method = "PUT")]
SampleItem Update(int id, SampleItem instance);
[WebInvoke(UriTemplate = "?id={id}", Method = "DELETE")]
void Delete(int id);
}
}
Service implementation
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class SampleService : ISampleService
{
private IDatabase db;
public SampleService(IDatabase db)
{
this.db = db;
}
public SampleService()
{
}
public List<SampleItem> GetCollection()
{
return db.Items.Values.ToList();
}
public SampleItem Create(SampleItem instance)
{
// Add the new instance of SampleItem to the collection
db.Items.Add(instance.Id, instance);
return db.Items[instance.Id];
}
//..Rest omitted..
}
Database interface:
using RestService2.Entities;
namespace RestService2.Service
{
public interface IDatabase
{
Dictionary<int, SampleItem> Items { get; }
}
}
Database implementation:
public class Database : IDatabase
{
private Dictionary<int, SampleItem> items;
public Database()
{
}
public Dictionary<int, SampleItem> Items
{
get
{
return items;
}
}
}
..and the global.asax file
namespace RestService2.Web
{
public class Global : HttpApplication
{
static IWindsorContainer Container {get; set;}
void Application_Start(object sender, EventArgs e)
{
BuildContainer();
RegisterRoutes();
}
private void BuildContainer()
{
Container = new WindsorContainer();
Container.AddFacility<WcfFacility>()
.Register(Component.For<ISampleService>().ImplementedBy<SampleService>().Named("SampleService"))
.Register(Component.For<IDatabase>().ImplementedBy<Database>());
}
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("SampleService",
new DefaultServiceHostFactory(), typeof(SampleService)));
}
}
}
The service contract, service implementation, database interface and database implementation are in assembly A, SampleItem (an entity) is defined in assembly B and the global.asax.cs is in assembly C.
I have added references from assembly A and B to assembly C.
When I try to access the service help page (or any service method for that matter) I get the following error message: Could not find a component with type RestService2.Service.SampleService, RestService2.Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null, did you forget to register it?
Any idea what could be problem? How should i configure the container correctly?
Regards
I was able to build a REST service using the WCFIntegration facility, using both interfaces for registration and through the MVC routing mechanism.
// SVC Routes
routes.Add(new ServiceRoute("Example", new WindsorServiceHostFactory<RestServiceModel>(), typeof(IExample)));
Remember to register the service/implementation in Windsor before calling RegisterRoutes (where this code would be). Additionally, make sure you call this route registration before your default route, otherwise that will be used instead.
The service can then just be called via the route, ie:
http://localhost:80/Core/Example/GetAll/
since you registered the routing by concrete type
RouteTable.Routes.Add(new ServiceRoute("SampleService",
new DefaultServiceHostFactory(), typeof(SampleService)));
and as far as I remember you cannot do otherwise... I mean you cannot register by interface, you need to register into the container by concrete as well
instead of
.Register(Component.For<ISampleService>().ImplementedBy<SampleService>().Named("SampleService"))
try
.Register(Component.For<SampleService>().Named("SampleService"))

Self-host (No IIS or WAS) WCF with a service that requires parameters

Hopefully this is an easy one. I'm wondering if this is possible - perhaps it is not. I'm attempting to self-host a WCF service (in my example below it is a console application). The service does not have a default constructor. It only contains a single parameter signature constructor. I need the service to be able to handle user sessions. Currently I am using Ninject DI. Here is a simple code solution I came up with to demonstrate my issue:
using System;
using System.ServiceModel;
using System.ServiceModel.Web;
using Ninject.Modules;
namespace ConsoleApplication1
{
public class Program
{
static void Main()
{
using (var webServiceHost = new WebServiceHost(typeof(MyWcf)))
{
var webHttpBinding = new WebHttpBinding();
var uri = new Uri("http://localhost:8000/");
webServiceHost.AddServiceEndpoint(typeof(IMyWcf), webHttpBinding, uri);
webServiceHost.Open();
Console.WriteLine("Service is ready...");
Console.ReadKey();
}
}
}
[ServiceContract]
public interface IMyWcf
{
[OperationContract, WebGet(UriTemplate = "")]
string HelloWorld();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyWcf : IMyWcf
{
private readonly IMessage _customMessage = new Message("Default Message.");
public MyWcf(IMessage message)
{
_customMessage = message;
}
public string HelloWorld()
{
return _customMessage.Text;
}
}
public interface IMessage
{
string Text { get; }
}
public class Message : IMessage
{
public Message (string message)
{
Text = message;
}
public string Text { get; set; }
}
public class NinjectSetup : NinjectModule
{
public override void Load()
{
Bind<IMessage>().To<Message>()
.WithConstructorArgument("message", "Injected String Message.");
}
}
}
Obviously commenting out the parameterized constructor allows the service to run. But that does me no good. I don't want to use ServiceHostFactory because that apparently requires me to have a .svc/IIS. Is there a way around this? Can I just create a new MyWebServiceHost that inherits from WebServiceHost and override some method that will create a instance for the service?
Using Ruben's suggestion (in the comments) above, I was able to locate a working example within the Ninject.Extensions.Wcf source repository.