Using a ChannelFactory with Simple Injector - c#

I have some integration tests that run by using ChannelFactory to create an instance of the service.
My normal WCF Service is setup using Simple Injector, but I can't seem to figure out how to get that to work with my ChannelFactory code.
Here is what my test setup code looks like:
private readonly IMyService myChannel;
public TestingClient(Binding binding, string serviceUrl)
{
var endpointAddress = new EndpointAddress(serviceUrl);
var myChannelFactory = new ChannelFactory<IMySErvice>(binding, endpointAddress);
myChannel = myChannelFactory.CreateChannel();
}
When I run my tests it looks like myChannel.MyServiceOperation(); But I get an error of:
The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor
This is because normally WCF does not support constructors with parameters. But SimpleInjector fixes that and allows for parameters that it knows how to construct.
Because of this MyService has a constructor that looks like this:
public MyService(Validations validations)
{
this.validations = validations;
}
And Validations is injected by Simple Injector (see here for how that is setup without a ChannelFactory)
But since I am setting it up manually with the ChannelFactory, I don't know how to get SimpleInjector in the mix (so it can inject the Validations object).
Can anyone tell me how to get Simple Injector to work with a ChannelFactory?

Related

MAUI dependency injection service resolve in Program.cs

MAUI has dependency injection setup similar to what ASP.NET Core has in the Startup.cs class. This one is set in MauiProgram.cs file by default.
My question is: How can I get a service instance in this file after services registration? I guess, one solution will be the following, but then I must edit this code also if the constrctors of these services change during time:
var keyValueStore = new PreferencesKeyValueStore();
var accountService = new AccountService(keyValueStore);
var profileService = new ProfileService(keyValueStore);
builder.Services.AddSingleton<IKeyValueStore>(keyValueStore);
builder.Services.AddSingleton<IAccountService>(accountService);
builder.Services.AddSingleton<IProfileService>(profileService);
//Here now I can use accountService and profileService to do something
I can not find more elegant solution that will return the service instance for me from the DI container. Something like:
builder.Services.AddSingleton<IKeyValueStore, PreferencesKeyValueStore>();
builder.Services.AddSingleton<IAccountService, AccountService>;
builder.Services.AddSingleton<IProfileService, ProfileService>();
//Now I can't perform something like: var accountService = diContainer.GetInstance<IAccountService>(); or similar.
I don't know how to reach di container and ask it to provide me registered instance.
Actually, the documentation provided a simple way to do so.
Check it here
They recommended to use the Handler property of any object of type Element, there you can write the code :
// Considering you want to resolve a service from your custom interface IMyService
var service = this.Handler.MauiContext.Services.GetService<IMyService>();
// Then you can use the resolved service..
But there are some issues, personally it never worked for me, the Handler property may be null because of the lifecycle of the Element you are calling it on.
To avoid this issue, use a full line like:
var service = Application.Current.MainPage
.Handler
.MauiContext
.Services
.GetService<IMyService>();
// Then you can use the resolved service..
This works fine for me
Hope it helps you ..

How to inject gRPC client to the singleton service?

Grpc.Net.ClientFactory package provides gRPC integration with the IHttpClientFactory and it comes with the convinient extension method used to register gRPC clients: IServiceCollection.AddGrpcClient<TClient>().
The problem is that this method registers TClient with the transient lifetime, which is, in turn, means that it's imposible to inject it into the singleton service. And looks like there is no possibility to configure it somehow (at least the AddGrpcClient source code has nothing on that).
So the question is: how to correclty inject the gRPC client to the singleton service and benefit from the IHttpClientFactory integration at the same time? Should I inject some client factory instead?
How about using named Grpc client to register the client, using like this.
// Register on the Startup.cs
services
.AddGrpcClient<Catalog.CatalogClient>("Catalog", o =>
{
o.Address = new Uri("https://localhost:5001");
});
// Create your service
public class OrderingService : IOrderingService
{
private readonly Catalog.CatalogClient _client;
public OrderingService(GrpcClientFactory grpcClientFactory)
{
_client = grpcClientFactory.CreateClient<Catalog.CatalogClient>("Catalog");
}
}
// Register the service as singleton at Startup.cs
service.AddSingleton<IOrderingService, OrderingService>();
// now _client instance is persist as long as you doesn't do anything weird with it
The grpc client might be a new instance each time (transient), but the underlying HttpClientMessageHandler "is managed".
I see no problems injecting a transient instance into a singleton service.

How to scope out Services that implement interface X in standard MS DI?

I'm using a lib that is based around standard MS DI stuff (which I see for the first time and not familiar with). So far it wasnt anything complicated, I build Service Provider:
var services = new ServiceCollection()
.AddSingleton<StartupService>()
.AddSingleton<Service2>()
.AddSingleton<Service3>()
.AddSingleton<Service4>()
.BuildServiceProvider();
Then I start the app via StartupService
await services.GetService<StartupService>().InitAsync();
And I inject services via ctor in all the modules or other services that dependent on these:
private readonly Service1 service1;
private readonly Service2 service2;
public OtherService(Service1 s1, Service2 s2)
{
service1 = s1;
service2 = s2;
}
It is nothing special so far but as number of services grew and some of them required "warm up" my StartupService started to looked grim:
public async Task InitAsync()
{
Service1.WarmUp();
Service2.WarmUp();
Service3.WarmUp();
...
}
So my ~~brilliant~~ dumb idea was to implement IWarmUp interface and just, like, iterate over service collection man:
foreach (var service in services.GetServices<IWarmUp>())
service.WarmUp();
Obviously it didnt work.
Is there a simple solution to that without making some complicated system or rewriting all my existing ctors that already use injection? How can I get the services that implement specific interface so I can make them do contract job?
You can register multiple implementations to a single service type - see here. So, you could use reflection to find all the types implementing IWarmUp, then loop through them and register each type to that service type.
foreach( var warmUpType in myListOfTypesImplementingIWarmUp )
serviceCollection.AddTransient( typeof(IWarmUp), warmUpType )
And then in InitAsync ask your IServiceProvider for an IEnumerable<IWarmUp> and loop through calling WarmUp on each object.
You can still register each type against itself like AddSingleton<Service2>() and then request it from DI as you're doing currently.

How to instantiate outside of a constructor?

How to replicate this code with Autofac syntax?
public static class MenuConfig
{
public static void Initialize()
{
var _menuService = DependecyFactory.GetInstance<IMenuService>();
Parameters.Menu = _menuService.Menu();
}
}
Before calling this a "duplicate question" please note that I'm looking for an Autofac command. I CANNOT inject the interface anywhere and then call "Resolve". What I need to is perform an "InstancePerRequest" inline and uninjected so I don't have to do this:
var _service = new Service(new Dependency(new context()));
LightInject has a method that allows instantiation from an interface OUTSIDE of a constructor like this:
var _service = DependecyFactory.GetInstance<IService>();
What is the equivalent method for Autofac?
When calling containerBuilder.Build() you get back a container which implements IContainer and ILifetimeScope, whenever you get hold of one of these interfaces, you can resolve types from it:
container.Resolve<IService>();
If you want this container to be static, you could add the container as a static property to the Program or Startup class (depending if you're creating a Console or ASP.NET application).
Remember that the root container will be around for the entire duration of your application, so this can result in unwanted memory leaks when used incorrectly. Also see the warning in the documentation.
Still, it's perfectly possible to do the memory management yourself by resolving an Owned<> version from your interface:
using (var service = Program.Container.Resolve<Owned<IService>>())
{
service.Value.UseService();
}
Anyway, since you mention a static class in the comments, the best solution is to change that into a non-static class and register it as a singleton with Autofac. Then you can inject a Func<Owned<IService>> serviceFactory into that singleton and create/dispose an instance of the service wherever you need it.
using (var service = serviceFactory())
{
service.Value.UseService();
}
This is simply not possible with Autofac. All other solutions involving Autofac will require code refactoring which may potentially break software functionality. So unfortunately, the most elegant and least disruptive solution is this:
var _service = new Service(new Dependency(new context()));
Since this is an edge case addressing only one part of the software, this compromise is acceptable. It would be nice, however, if Autofac implemented this functionality in some future release.

How to invoke WCF service with own constructor using my InstanceProvider

I am kind of new in implementing and using WCF services and extremely new (and apparently clueless) in DI.
I have WCF Services which are having constructors. The parameters of the constructors could only come in runtime from the Client application (Web server).
Something like this:
In Application server:
public class MyService : IMyService {
private IUserContext userContext;
public MyService(IUserContext uContext) {
this.userContext = uContext;
}
public DoWork() {
... // uses uContext
}
}
In Web server can only see IMyService and not the implementation of the MyService. The code would be something like this (oversimplified console app):
class Program {
static void Main(string[] args) {
var factory = new ChannelFactory<IMyService>("MyServiceEndpoint"); // MyServiceEndpoint correctly defined in config file
var client = factory.CreateChannel();
client.DoWork();
((IClientChannel)client).Close();
factory.Close();
}
}
First WCF "forced" me to use parameter-less constructor in the implementation of MyService in order to test it I added that by initializing the UserContext object. Of course I don't have the necessary info to create the object in compile time so this won't help me.
I proceeded with using this solution creating my own ServiceHostFactory, ServiceHost and IInstanceProvider where IDependency is an interface IUserContext which is implemeted by my UserContext class.
This works as expected, I registered in my svc file the custom factory, I don't need parameter-less constructor anymore. However since I don't know how to pass my UserContext to the InstanceProvider I only get a default UserContext object.
Now my noviceness comes in. I don't know how to invoke MyService by passing in the UserContext which lives in the web server. Do I also need own ChannelFactory?
Can someone direct me in the right way by updating the web server dummy code?
Thanks!
Remark: I don't want UserContext to be a parameter of the DoWork() method, because that would mean changing the parameter list of all my services and all calls...
The notion of constructors does not exist on the wire (no matter what transport you are using). For that reason you will never be able to make the client invoke a particular constructor. This is simply not part of the design of WCF (also not part of SOAP).
Don't use constructor parameters that are provided by the client. Or, make the service class have a parameterless ctor and make all service methods accepts the former constructor parameters as normal parameters.
You can also transmit common parameters as SOAP headers. That saves you changing the signature of all service methods.

Categories