Access to signalr hub from other part of my application - dependency-injection

I am writing a blazor server web app.
I have wrote this very basic signalr hub:
using Microsoft.AspNetCore.SignalR;
public class MyHub: Hub
{
public override Task OnConnectedAsync()
{
...
return base.OnConnectedAsync();
}
public Task my_function()
{
...
}
....
}
It works fine at this step.
Now, i want to access to this hub from another part of my blazor application:
public class AnotherClass
{
private readonly IHubContext<MyHub> _hub;
public AnotherClass(IHubContext<MyHub> hub)
{
_hub = hub;
}
...
public void another_function()
{
_hub.Clients.All.SendAsync(...); // <- This line works fine
(_hub as MyHub).my_function(); // <- Does not work
}
}
As you can see, i am working with dependency injection to access to my hub object.
I have read a lot of documentation, including Microsoft's official documentation.
Everybody says we have to work with IHubContext type in this kind of dependency injection.
So _hub object does not know my_function method...
I have tried a lot of things:
Cast _hub to MyHub
Declare a IMyHub interface and work with IMyHub in dependency injection
etc.
Nothing works...
My question is: How can i call my_function from AnotherClass object ?
Thanks a lot

Here's how I did it.
Create an interface for your hub:
public interface IMyHub
{
Task MyFunction(int parameter);
}
Then your hub implements the interface like so:
public class MyHub : Hub<IMyHub>
{
public async Task MyFunction(int parameter)
{
// do stuff
}
}
Then inject the hub into other classes like so:
private readonly IHubContext<MyHub, IMyHub> _myHub;
And you can invoke your function via:
_myhub.Clients.All.MyFunction(someInt);
This is based on the Strongly Typed Hubs and Send Messages From Outside a Hub documentation.
Functions you add to your IMyHUb interface don't necessarily have to have implementation code in the MyHub class. For instance, with
public interface IMyHub
{
Task MyFunction(int parameter);
Task MySecondFunction();
}
in your other class you can invoke
_myhub.Clients.All.MySecondFunction();
without any new code in the MyHub class.

How can i call my_function from AnotherClass object ?
You can't. The Hub is only called in response to a client invocation. There is no way to call a hub method manually.
If there is logic you want to run both inside the hub and outside the hub, you will need to refactor your code so it is some shared class that the Hub and AnotherClass can access.

Related

Resolve all already created service instances from .NET service provider

I would like to request all created instances from a transient service via the IServiceProvdier. My problem is that requesting them seems to create additional instances instead of retrieving only the already existing instances.
I have a service interface and implementation
public interface ISomeService {}
public class SomeService : ISomeService
{
public SomeService()
{
}
}
It is registered transient
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISomeService, SomeService>();
}
Another service where I try to get all already created services
public class AnotherService
{
// calls the constructor of SomeService
//public AnotherService(IEnumerable<ISomeService> instances) {}
public AnotherService(IServiceProvider serviceProvider)
{
// calls the constructor of SomeService
//IEnumerable<ISomeService> instances = serviceProvider.GetServices<ISomeService>();
// calls the constructor of SomeService
//IEnumerable<ISomeService> instances = serviceProvider.GetRequiredServices<ISomeService>();
}
}
I don't know why the constructor of SomeService is called but it definitly seems to do it due to the calls of Get...
Anyone managed to get the list of instances without creating one?
By definition a transient service will always be created each time you request the service provider or the DI system to resolve it.
If you want to reuse the same instances you can register services with a different lifetime
services.AddSingleton<ISomeService, SomeService>();
or
services.AddScoped<ISomeService, SomeService>();
if you register the dependency as a singleton than there will be a single instance of there service for the entire application lifetime.
if you register the dependency with the scoped lifetime than a new instance will be created for each scope. In Asp.Net a scope consists of a request.
If you want your dependency to be transient and want to have track of all the instances that have been created you can do a little trick using static references:
public static class SomeServiceReferences {
public static readonly IList<ISomeService> References { get; } = new List<ISomeService>();
}
public class SomeService: ISomeService {
public SomeService() {
SsomeServiceReferences.References.Add(this);
}
}
but I don't reccomend this approach cause holding the reference of those dependencies might cause performance problems and if you need to do such a thing there might be some problems with the design of your application.

DI parameters to class library without controller

So I'm not sure if I'm just missing something, but basically every example of DI I see with asp.net core shows passing of parameters from the appSettings.json file through a constructor in the controller and then to anything else.
Can I bypass the Controller and just inject directly a Class Library?
For an example of what I'm trying to do, let's say I have appSettings.json with
"EmailSettings":{"smtpServer":"mail.example.com", "port":123, "sendErrorsTo":"errors#example.com"}
Then a Class Library for EmailServices
EmailSettings.cs
public class EmailSettings{
public string smtpServer {get;set;}
public int port {get;set;}
public string sendErrorsTo {get;set;}
}
IEmailService.cs
public interface IEmailService
{
void SendErrorEmail(string method, Exception ex);
}
and EmailService.cs
public class EmailService :IEmailService
{
private readonly EmailSettings _emailSettings;
public EmailService(EmailSettings emailSettings)
{
_emailSettings = emailSettings;
}
public void SendErrorEmail(string method, Exception ex)
{
....
}
}
Startup.cs in the main asp.net core application
public void ConfigureServices(IServiceCollection services)
{
...
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
services.AddScoped<IEmailService, EmailService>(p => {
return new EmailService(p.GetService<EmailSettings>());
});
...
}
Without loading the EmailServices or the appsetting.json parameters through the controller and then into the BusinessLayer class library, I want to be able to call the SendErrorEmail from BusinessLayer (or any other place).
DoWork.cs
public MakeItWork()
{
try
{...}
catch (exception ex)
{
IEmailService.SendErrorEmail("BAL - MakeItWork",ex)
}
}
But it just fails with a null exception. The DI in the startup doesn't create the EmailService in place of the IEmailService, and I'm going to guess the parameters are not there either.
Thanks for any help you can give.
----EDIT----
I ended up just switching to using AutoFac for DI. It's able to accomplish what I was looking for. Accepted the answer below to give Phantom the points for trying to assist.
A couple of things:
In your MakeItWork() method, you have code that "calls" a method using the interface name - not even sure how that will compile. You need to use an object of a class that implements that interface to actually make method calls at runtime. For example, in your DoWork class, you could have a constructor requesting for an instance of a class that implements the IEmailService interface and store it for future use in other methods.
Second, in the Services collection, you are adding a "Scoped" dependency (in the ConfigureServices method). A "scoped" dependency is only created upon a (http)Request, typically via calls to controllers. From your code and your explanation, it looks like you are wanting to add a Singleton object for your IEmailService interface. So, instead of adding a Scoped dependency use AddSingleton - as you have done, you can also create the specific object in the call to AddSingleton - that means this object will be provided every time you request it (through class constructors, for example). If you are using it as a singleton, you should also make sure that it is thread safe. Alternatively, you can also add the dependency using AddTransient - if you use this, a new object will be created every time you request it.
Update:
Sample Code
Modify your ConfigureServices to make the EmailService as Transient (this means a new object every time this service is requested):
public void ConfigureServices(IServiceCollection services)
{
...
services.Configure<EmailSettings>(Configuration.GetSection("EmailSettings"));
services.AddTransient<IEmailService, EmailService>();
...
}
Your "DoWork" class should request the EMail Service in the constructor:
public class DoWork()
{
private IEmailService _emailService;
//Dependency should be injected here
public DoWork(IEmailService emailService)
{
_emailService = emailService;
}
public MakeItWork()
{
try
{...}
catch (exception ex)
{
//Use the saved email service object to do your work
_emailService.SendErrorEmail("BAL - MakeItWork", ex)
}
}
}
It doesn't end here. The question remains as to how you are going to create an Object of the DoWork class. For this, one idea is to create an interface for the DoWork class itself and then setup the container for that interface as well. Then wherever you would want to use the DoWork implementation you can "request" the interface for DoWork. Or use the container directly to create an instance.

NServiceBus & Bootstrapper StructureMap

I am using NServiceBus is an azure worker role via convention by having configuration in app.config and azure Queue details in .csdef.
I have a rest service that accesses the IBus by doing this:
Configure.Instance.Builder.Build<IBus>()
and works fine!
I have a class that implements IWantToRunAtStartup where I do the configure bootstrapper as follows:
Bootstrapper.With.StructureMap()
.UsingAutoRegistration()
.And.AutoMapper().Start();
I'm losing the IBus reference if I then use the bootstrap container:
Configure.Instance.StructureMapBuilder((IContainer) Bootstrapper.Container);
How do I use Bootsrtrapper.StructureMap and NServiceBus?
Telling us which container you want to use needs to be done from a IConfigureThisEndpoint and IWantCustomInitialization class, example:
public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantCustomInitialization
{
public void Init()
{
Configure.With()
.StructureMapBuilder((IContainer) Bootstrapper.Container);
}
}

Generic Service Implementation

In my learning ASP.NET MVC 4 application, I use repository pattern and service layers. Entity framework and Autofac is used in the project. My data classes are simple and almost all operations are basic CRUD operations.
I have an abstract repository base as:
public abstract class RepositoryBase<T> where T : class
And this is a sample repository for the entity Sample1:
public class Sample1Repository : RepositoryBase<Sample1>, ISample1Repository
{
public Sample1Repository(IDatabaseFactory databaseFactory)
: base(databaseFactory)
{
}
}
public interface ISample1Repository : IRepository<Sample1>
{
}
This is my controller:
public class SampleController : Controller
{
private readonly ISampleService _sampleService;
public SampleController(ISampleService sampleService)
{
this._sampleService = sampleService;
}
}
And, lastly this is my service:
public interface ISampleService
{
IEnumerable<Sample1> GetSample1s();
Sample1 GetSample1(int id);
void CreateSample1(Sample1 item);
void DeleteSample1(int id);
void SaveSample1();
}
public class SampleService : ISampleService
{
private readonly ISample1Repository _sample1Repository;
private readonly IUnitOfWork _unitOfWork;
public SampleService(ISample1Repository sample1Repository, IUnitOfWork unitOfWork)
{
this._sample1Repository = sample1Repository;
this._unitOfWork = unitOfWork;
}
}
Now, I have these questions:
1) Do I need to create a separate class for each entity repository. (ie. another Sample2Repository for the entity Sample2)
2) Can I use a generic service for doing those CRUD tasks?
3) If generic service is possible. How can I register it/them in Autofac bootstrapper?
1) Do I need to create a separate class for each entity repository.
If you can, create a single generic IRepository<T> implementation and map that to this open generic interface.
2) Can I use a generic service for doing those CRUD tasks?
Of course you can, but the real question is: is it useful to do so? Seems to me that your ISampleService simply duplicates the logic from the IRepository<T>. It probably forwards to this repository. Seems like a useless abstraction to me. If your application is truly CRUD, you can inject your repository directly into your controller.
3) If generic service is possible. How can I register it/them in
Autofac bootstrapper?
You can map an open-generic interface to an open generic implementation as follows:
builder.RegisterGeneric(typeof(EntityFrameworkRepository<>))
.As(typeof(IRepository<>))
If you have many concrete (non-generic) IRepository<T> implementations and want to batch-register them, you can do this as follows:
builder.RegisterAssemblyTypes(typeof(IRepository<>).Assembly)
.AsClosedTypesOf(typeof(IRepository<>));

How do you output the context class using log4net as a service?

I am using Log4Net as a service which is injected into other services using StructureMap.
How do I ensure the log file includes the calling service class context (class name and/or thread) which is making the log4net calls?
Surely the calling class or thread will always be the logging service which doesn't help me understand where the logging calls are really coming from.
EDIT:
Register code:
ObjectFactory.Initialize(x =>
{
x.For<ILog>().AlwaysUnique().Use(s => s.ParentType == null ?
LogManager.GetLogger(s.BuildStack.Current.ConcreteType) :
LogManager.GetLogger(s.ParentType));
});
Service layer:
public class LoggerService : ILoggerService
{
private readonly ILog log;
public LoggerService(ILog logger)
{
log = logger;
log.Info("Logger started {0}".With(logger.Logger.Name));
}
public void Info(string message)
{
log.Info(message);
}
}
In the logging, I am still always getting the LoggerService as the context so I'll never see what actually called the logger. It doesn't seem to be working correctly. I feel like I'm missing something here...
Edit 2:
I've added a pastie link for a console app here:
http://pastie.org/1897389
I would expect the parent class to be logged but it isn't working at the simplest of levels.
You might want to have a look at Castle Dynamic proxy in order to solve it using AOP. There is an example of using it with Structure Map on the Structure Map Google Group.
Ayende has an example of AOP based logging using Log4Net and Windsor.
I use StructureMap in a lot of the code I generate and I have a StructureMap registry which I use to hook the logger into the context of the class that it is injected into.
For Reference, I'm using the 2.6.2 version of StructureMap but should be fine with 2.5+ where the new .For<>().Use<>() format is utilized.
public class CommonsRegistry : Registry
{
public CommonsRegistry()
{
For<ILogger>().AlwaysUnique().Use(s => s.ParentType == null ? new Log4NetLogger(s.BuildStack.Current.ConcreteType) : new Log4NetLogger(s.ParentType.UnderlyingSystemType.Name));
XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(GetType()).Location), "Log.config")));
}
}
What this registry is doing is for anywhere the ILogger is injected, use the class that it's injected into is where the logging messages are logged to/context of.
*Also, in the second line (XmlConfigurator.ConfigureAndWatch) is where I tell Log4Net to get the logging information from the file "Log.config" instead of the application configuration file, you may or may not like that and can be omitted.
The code I use is a common IOC.Startup routine where I would pass if I would like to use the default registery.
ObjectFactory.Initialize(x =>
{
x.AddRegistry<CommonsRegistry>();
...
}
This gives me the calling class name in the logging instance where messages are logged to automatically and all that is required is to inject the logger into the class.
class foo
{
private readonly ILogger _log;
public foo(ILogger log)
{
_log = log;
}
}
Now the messages are logged as context/class "foo".

Resources