.NET Core + WCF integration with DI Problem - c#

I have an problem to declare dependency injection. I have to declare a WCF service and I did it like this:
services.AddTransient<IService, ServiceClient>();
As I will need to work with the WCF header I need to add a behavior to check the headers. Normally I would do it like this:
var client = new ServiceClient();
client.Endpoint.Behaviors.Add( new HeaderInspectionBehavior());
But I can't do it that way because I'm getting the IService injected in the constructor.
I tried to do it this way:
var client = new ServiceClient();
client.Endpoint.Behaviors.Add(new HeaderInspectionBehavior());
services.AddTransient<IService, ServiceClient>(sp => client);
But it didn't work, in the second WCF call it changes the state to "FAULT" or "CLOSED".
Does anyone know of another way to do this?

Based in this i found a solution that looks like this:
ServiceClient Config(IServiceProvider _) => new ServiceClient(..., ...);
services.AddTransient<IService, ServiceClient>(Config);
It's work for me.

Related

protobuf-net.grpc Client Injection

I was previously using Grpc.AspNetCore but am trying to migrate to protobuf-net.grpc because I much prefer the code-first approach given that it's more idiomatically dotnet and the whole solution is built on dotnet, so having .proto files and code-gens just causes a bit of a mess.
So I've created my service as per this guide:
[ServiceContract]
public interface IAdService
{
[OperationContract]
Task<HorseSaleAdReply> GetHorseSaleAds(AdRequest adRequest, CallContext context = default);
}
As far as I can tell, this works nicely.
However, I now want to create a client for this service and have it injected. The docs suggest this approach:
using var channel = GrpcChannel.ForAddress("https://localhost:7184");
var client = channel.CreateGrpcService<IGreeterService>();
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" });
However, when I try and wrap that in a singleton:
builder.Services.AddSingleton(_ => {
using var channel = GrpcChannel.ForAddress("https://localhost:7184");
return channel.CreateGrpcService<IAdService>();
})
And inject it into one of my classes, I get an error about the channel having already been disposed.
Would you be able to advise on how I can correctly inject these clients?
My only other idea would be to create a client class that implements IDisposable, stores a reference to that channel and does it that way. However, this seems like it really ought to be unnecessary.

Adding a SocketsHttpHandler to an HttpClient created via AddHttpClient in Startup.cs?

I'd like to add a SocketsHttpHandler to an HttpClient that I am creating via AddHttpClient in Startup.cs. The point of that is to be able to inject instrumentation etc. in the HttpClient via a factory.
This does not give me the option to use the constructor of HttpClient, which is where you'd usually add a SocketsHttpHandler.
Also, I don't see any obvious property etc. I can use to add it later.
...aaaand as is traditional I found the answer five minutes later. Never matters how long you look before, does it now?
How I can change configuration of HttpMessageHandler from Polly retry?
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
{
MaxConnectionsPerServer = 3,
})

Customising instantiation of WCF web service

I have a WCF RESTful service that I want to integration test so need to create an instance of the service locally within the test with a reference to a client channel that I can call. I can do this, however the code I'm using will only call the Services default parameterless constructor as below
_serviceHost = new WebServiceHost(typeof(UserService), baseAddress);
var binding = new WebHttpBinding();
_serviceHost.AddServiceEndpoint(typeof(Interface.IUserService), binding, address.Uri);
_serviceHost.Open();
In my UserService class, I want to inject a dependency into it for the data repository, as so
public UserService(IUserDataRepository userRepository)
{
_userRepository = userRepository;
}
How can I adapt the first lot of code so that I can create and self host my WCF REST service with an IDataRepository object that I create (Mock) in the test class?
Well, I think you can use the same approach as you have for production environment. I might be wrong but you must already have your custom ServiceHost and ServiceHostFactory. If not please take a look at this article Using Instance Provider and ServiceHostFactory to Construct the Service. You want to read through steps steps 1-3. You will need to derive from WebServiceHost to implement your own one. Then your code will look like that:
_serviceHost = new YourCustomServiceHost(typeof(UserService), baseAddress);
Hope it helps!

Ninject WCF bootstrapper registering multiple services

I'm having a problem getting the wcf extensions to work with more than one self host bootstrapper. With one my services are created by ninject fine (per call), but when I add another I get an exception that the ChannelDispatcher is unable to open its IChannelListener, the inner exception states that a registration already eixsts for URI 'net.tcp://localhost:901/MyService'.
My registration code looks like this:
var myService= NinjectWcfConfiguration.Create<MyService, NinjectServiceSelfHostFactory>();
_myServiceHost= new NinjectSelfHostBootstrapper(() => _kernel, myService);
var myService2= NinjectWcfConfiguration.Create<MyService2, NinjectServiceSelfHostFactory>();
_myService2Host= new NinjectSelfHostBootstrapper(() => _kernel, myService2);
_myServiceHost.Start();
_myService2Host.Start();
Both services have the correct sections in the config file and they both have different endpoint URIs with different ports. The same config works fine if I wire all of this up manually.
Does anyone have a clue here? Bit stumped...
Cheers
I ran into this issue just now, the solution is to have one Bootstrapper with all configurations in its params:
var myService= NinjectWcfConfiguration.Create<MyService, NinjectServiceSelfHostFactory>();
var myService2= NinjectWcfConfiguration.Create<MyService2, NinjectServiceSelfHostFactory>();
_myServicesHost= new NinjectSelfHostBootstrapper(() => _kernel, myService, myService2);
_myServicesHost.Start();
Another alternative is to use separate Kernel for each NinjectSelfHostBootstrapper instance
var myService= NinjectWcfConfiguration.Create<MyService, NinjectServiceSelfHostFactory>();
_myServiceHost= new NinjectSelfHostBootstrapper(() => new StandardKernel(YourInjectionModule), myService);
var myService2= NinjectWcfConfiguration.Create<MyService2, NinjectServiceSelfHostFactory>();
_myService2Host= new NinjectSelfHostBootstrapper(() => new StandardKernel(YourInjectionModule), myService2);
_myServiceHost.Start();
_myService2Host.Start();
Also, when you dispose NinjectSelfHostBootstrapper _myServiceHost.Dispose() its Kernel will also be disposed. So if you use your kernel somewhere else you will run into problems.

Is it possible to configure a WCF service using castle windsor fluent configuration without config or svc files?

I have an ASP .Net MVC 3.0 web application hosted on IIS and I am using Castle Windsor version 3.0.
What I would like to do is register a WCF service using the webHttpBinding without any entries in the web.config or having a .svc file. Is this possible?
I tried this in my IWindsorInstaller implementation:
container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero);
container.Register(
Component
.For<IMyService>()
.ImplementedBy<MyService>()
.AsWcfService(new DefaultServiceModel()
.AddBaseAddresses("http://localhost/WebApp/Services")
.AddEndpoints(WcfEndpoint
.BoundTo(new WebHttpBinding())
.At("MyService.serv"))
.Hosted()
.PublishMetadata()));
And I am ignoring anything that finishes serv like so in my RegisterRoutes method in global asax:
routes.IgnoreRoute("{resource}.serv/{*pathInfo}");
If I point a browser at http://localhost/WebApp/Services/MyService.serv I get a 404.
What am I doing wrong or am I trying to do something stupid (or not possible or both!)?
Thanks to Ladislav's suggestion of using the ServiceRoute I have worked out how to do this, but I am not sure it is ideal, here is what I have done (in case Googlers find it and can improve it etc):
Created an extension method on ComponentRegistration like so:
public static ComponentRegistration<T> AddServiceRoute<T>(
this ComponentRegistration<T> registration,
string routePrefix,
ServiceHostFactoryBase serviceHostFactory,
string routeName) where T : class
{
var route = new ServiceRoute("Services/" + routePrefix + ".svc",
serviceHostFactory,
registration
.Implementation
.GetInterfaces()
.Single());
RouteTable.Routes.Add(routeName, route);
return registration;
}
What that does is adds a service route that places the service under the Services folder and tacks on a .svc extension (I will probably remove that). Note, I am assuming that the service implements only one interface, but in my case that is fine and I think is good practice anyway.
I'm not sure this is the best place to have this extension method, or even if an extension method is actually needed at all - perhaps I should be doing it with a service host builder or something, I don't know!
Then in the MapRoute calls I made sure to add this to the constraints parameter as per the question MVC2 Routing with WCF ServiceRoute: Html.ActionLink rendering incorrect links!
new { controller = #"^(?!Services).*" }
This just makes anything that starts Services not get matched as a controller. I do not like this very much since I would have to add it to all of my routes - I would rather globally make the Services folder fall into the service resolver or something (I do not know enough about MVC it seems!).
Finally in my windsor installer I register the service like so:
container.AddFacility<WcfFacility>(
f =>
{
f.Services.AspNetCompatibility =
AspNetCompatibilityRequirementsMode.Allowed;
f.CloseTimeout = TimeSpan.Zero;
});
container.Register(
Component
.For<IMyService>()
.ImplementedBy<MyService>()
.AsWcfService(new DefaultServiceModel()
.AddEndpoints(WcfEndpoint.BoundTo(new WebHttpBinding()))
.Hosted()
.PublishMetadata())
.AddServiceRoute("MyService", new DefaultServiceHostFactory(), null));
After that I can browse to the service and it is picked up and constructed fine!
As I allude to though, it might not be the best way to do this, but it works.

Categories

Resources