I'm in the process of upgrading my application from NServiceBus 4 to 5.
I have a class that implements IWantToRunWhenBusStartsAndStops and on the Start() method I print out the EndpointName - taking it from NServiceBus.Configure.EndpointName
On NServiceBus 5 it is deprecated and I want to do it correctly. How can I get the EndpointName?
You can use a ReadOnlySetting instance, take a look at the following sample:
class MyClass : IWantToRunWhenBusStartsAndStops
{
public ReadOnlySettings Settings{ get; set; }
public void Start()
{
var name = this.Settings.EndpointName();
}
public void Stop()
{
}
}
Where EndpointName() is an extension method provided by NServiceBus in the NServiceBus namespace.
Related
I want to inject my strongly typed hub in a service, but I don't like certain thing in the example shown by Microsoft - https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-2.2 (Inject a strongly-typed HubContext)
public class ChatController : Controller
{
public IHubContext<ChatHub, IChatClient> _strongChatHubContext { get; }
public ChatController(IHubContext<ChatHub, IChatClient> chatHubContext)
{
_strongChatHubContext = chatHubContext;
}
public async Task SendMessage(string message)
{
await _strongChatHubContext.Clients.All.ReceiveMessage(message);
}
}
In this example ChatHub is coupled to ChatController.
So I want to inject the hub itself
defined with generic interface parameter and no concrete implementation of it will be defined in my service.
This is sample code
public interface IReportProcessingClient
{
Task SendReportInfo(ReportProgressModel report);
}
public class ReportProcessingHub : Hub<IReportProcessingClient>
{
public async Task SendMessage(ReportProgressModel report)
{
await Clients.All.SendReportInfo(report);
}
}
public class ReportInfoHostedService : IHostedService, IDisposable
{
private readonly Hub<IReportProcessingClient> _hub;
private readonly IReportGenerationProgressService _reportService;
public ReportInfoHostedService(Hub<IReportProcessingClient> hub, IReportGenerationProgressService reportService)
{
_hub = hub;
_reportService = reportService;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_reportService.SubscribeForChange(async x =>
{
await _hub.Clients.All.SendReportInfo(x);
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void Dispose()
{
}
}
This approach obviously will need additional registration of the hub in Startup.cs as it is not called by the context api provided by Microsoft.
services.AddSingleton<Hub<IReportProcessingClient>, ReportProcessingHub>();
app.UseSignalR(route => {
route.MapHub<ReportProcessingHub>("/reportProcessingHub");
});
All done and working until the hub is trying to send messages to Clients. Then I get the exception
_hub.Clients.All threw an exception of System.NullReferenceException: 'Object reference not set to an instance of an object.'
So to summerize:
1. Is this the right way to inject strongly typed hubs and what am I doing wrong(e.g. wrong registration of the hub in services, wrong usage of app.UseSingleR)?
2. If not, what is the correct way?
NOTE:
I know there is a lot easier way injecting IHubContext<Hub<IReportProcessingClient>>, but this is not a solution for me, because I have to call the hub method name passed as string parameter.
I want to ... and no concrete implementation of it will be defined in my service
If you don't want to expose a concrete hub implementation, you should at least expose a base class or interface. However, since a hub should inherit from the Hub class, we can't use an interface here. So let's create a public base hub ReportProcessingHubBase as well as an internal concrete ReportProcessingHub:
public abstract class ReportProcessingHubBase : Hub<IReportProcessingClient>
{
public abstract Task SendMessage(ReportProgressModel report);
}
// the concrete hub will NOT be exposed
internal class ReportProcessingHub : ReportProcessingHubBase
{
public override async Task SendMessage(ReportProgressModel report)
{
await Clients.All.SendReportInfo(report);
}
}
Make sure you've registered the two related service:
services.AddScoped<ReportProcessingHubBase, ReportProcessingHub>();
services.AddHostedService<ReportInfoHostedService>();
Make sure you're mapping the Base Hub (MOST IMPORTANT!):
endpoints.MapHub<ReportProcessingHubBase>("/report");
Finally, you can get the base hub by injecting IHubContext<ReportProcessingHubBase,IReportProcessingClient> :
public class ReportInfoHostedService : IHostedService, IDisposable
{
private readonly IHubContext<ReportProcessingHubBase,IReportProcessingClient> _hub;
public ReportInfoHostedService(IHubContext<ReportProcessingHubBase,IReportProcessingClient> hub)
{
_hub = hub;
}
...
}
Now, you can invoking the hub method in a strongly-typed way.
I am very new to Autofac and not able to understand the syntax for registration. I have following calsses/interfaces :
//Interface
public interface ISender
{
void Send();
}
//implementations
public class Post : ISender
{
public void Send()
{
//Post implementation
}
}
public class Email : ISender
{
public void Send()
{
//Email implementation
}
}
And a class that calls these implementations
public class Consumer
{
ISender Sender;
public Consumer(ISender sender)
{
Sender = sender
}
public void Act()
{
Sender.Send();
}
}
Now, which implementation to call is to be decided in a controller, so I tried using IIndex from this page like:
public calss PostController
{
Consumer ConsumerObject;
public PostController(IIndex<string, Consumer> consumer)
{
ConsumerObject = consumer["post"];
}
}
public class EmailController
{
Consumer ConsumerObject;
public PostController(IIndex<string, Consumer> consumer)
{
ConsumerObject = consumer["email"];
}
}
Firstly, is it correct or doable? Now the problem is I don't understand how to register in Autofac. So, How can we register Consumer and ISender in Autofac ?? Please suggest if there is any better/alternative way.
The way of registering components in Autofac is described widely in the documentation here. And how to use keyed services is described in the documentation you linked.
Basically you have to create ContainerBuilder, register all your components and build the container itself, based on the project type you have.
In your situation you need to use the following registrations:
var builder = new ContainerBuilder();
builder.RegisterType<Post>().Keyed<ISender>("post");
builder.RegisterType<Email>().Keyed<ISender>("email");
builder.RegisterType<Consumer>();
If you use ASP.NET WebApi (I assume that based on the fact you are using Controllers), you need to register your controllers
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
or for MVC
builder.RegisterControllers(typeof(MvcApplication).Assembly);
Now, there are various way to pick the Consumer with proper ISender implementation (I assume you want to pick proper implementation in controller).
One would be to inject IIndex<string, ISender> to Consumer class and pass the proper key string to it as well:
public class Consumer
{
ISender Sender;
public Consumer(string type, IIndex<string, ISender> sender)
{
Sender = sender[type];
}
public void Act()
{
Sender.Send();
}
}
Then, in controller you could use Func injection:
public class PostController
{
Consumer ConsumerObject;
public PostController(Func<string, Consumer> consumer)
{
ConsumerObject = consumer("post");
}
}
public class EmailController
{
Consumer ConsumerObject;
public EmailController(Func<string, Consumer> consumer)
{
ConsumerObject = consumer("email");
}
}
Another could be registering Consumer twice with Register method and resolving ISender in registration time.
I am trying to use the new ASP.NET 5 dependency injection system, but it seems limited to ONLY constructors of classes that inherit from Controller.
Is there any other way to inject things? Properties? Anything? This is so severely limiting and has had me brickwalling for days.
Just tested this (RC1 Update1), it works with other classes as well.
I wrote a small example, first the type declarations:
public interface IBaseServiceType { }
public interface IComposedServiceType
{
IBaseServiceType baseService { get; }
}
public class BaseServiceImplementation : IBaseServiceType { }
public class ComposedServiceImplementation : IComposedServiceType
{
public IBaseServiceType baseService { private set; get; }
public ComposedServiceImplementation(IBaseServiceType baseService)
{
this.baseService = baseService;
}
}
The configuration:
services.AddTransient(typeof(IBaseServiceType), typeof(BaseServiceImplementation));
services.AddTransient(typeof(IComposedServiceType), typeof(ComposedServiceImplementation));
And create the instance like this where context is your HttpContext:
var composedServiceInstance = context.ApplicationServices.GetService<IComposedServiceType>();
Register your class as a service and treat it like you would all other services
see
Net Core Dependency Injection for Non-Controller
I want to bind my Entity Framework context to be scoped per NServicebus message. Would the following code successfully do that?
Bind<IDbContext>().To<MyContext>()
.InScope(x => x.Kernel.Get<IBus>().CurrentMessageContext.Id);
Background
I have a NServicebus service that has several IMessageHandlers that read IEvents off an MSMQ Queue.
Each handler converts the message and saves it to a MS SQL Database by way of a particular IRepository sitting over an Entity Framework context.
The repositories needed by each handler are injected via ninject using NServicebus.ObjectBuilder.Ninject
public class Product
{
public string Code { get; set; }
public Category Category { get; set; }
}
public class Category
{
public string Code { get; set; }
}
public class SampleContext : IDbContext
{
IDbSet<Product> Products { get; }
IDbSet<Category> Categories{ get; }
}
public class ProductRepository : IProductRepository
{
private IDbContext _context;
public ProductRepository(IDbContext ctx) { _context = ctx; }
public void Add(Product p)
{
_context.Products.Add(p);
_context.SaveChanges();
}
}
public class CategoryRepository : ICategoryRepository
{
private IDbContext _context;
public CategoryRepository (IDbContext ctx) { _context = ctx; }
public Category GetByCode(string code)
{
return _context.Categories.FirstOrDefault(x => x.Code == code);
}
}
public class AddProductMessageHandler : IMessageHandler<IAddProductEvent>
{
private IProductRepository _products;
private ICategoryRepository _categories;
public AddProductMessageHandler(IProductRepository p, ICategoryRepository c)
{
_products = p;
_categories = c;
}
public void Handle(IAddProductEvent e)
{
var p = new Product();
p.Code = e.ProductCode;
p.Category = _categories.GetByCode(e.CategoryCode);
_products.Add(p);
}
}
Issue
If the EF context is bound in Transient scope (default) then each bound repository in the handler has it's own instance of the context.
Bind<IDbContext>().To<SampleContext>();
This causes issues if I load an object from one repository and then save it via another.
Likewise, if it's bound in Singleton scope, then the same context is used by all repositories, but then it slowly fills up with tracked changes and goobles up all my ram (and gets slower and slower to boot).
Bind<IDbContext>().To<SampleContext>().InSingletonScope();
Question
Ideally I would like each message handler to have 1 EF context that all required repositories (of that handler) use to load and save entities.
Is scoping the context to the current messages Id property a safe/reliable/good way of doing this?
Bind<IDbContext>().To<SampleContext>()
.InScope(x => x.Kernel.Get<IBus>().CurrentMessageContext.Id);
See my blogpost here which describes the scoping apart from NSB 4.0
http://www.planetgeek.ch/2013/01/16/nservicebus-unitofworkscope-with-ninject/
If you have 3.0 you can look into the current develop branch and port the extension methods to your code. You only have to change the scope name.
I'm not familiar with EF Context so please disregard if the answer below does not make any sense.
If EF Context is similar to a NH ISession, then I think the better option is to use a UoW the same way as NH implementation.
You can read more about UoW here.
I've been trying to get the ninject working in wcf, using the wcf extension and the interception with dynamicproxy2 extension. I've basically created a Time attribute and have it all working in a basic scenario. Where I get trouble is when in ninject module I create my service binding with a constructor argument:
Bind<IMyDependency>().To<MyDependency>();
Bind<IService1>().To<Service1>().WithConstructorArgument("dependency", Kernel.Get<IMyDependency>());
Everything works fine, but the Time attribute wont fire on anything in my Service1 or MyDependency.
The time attribute is the standard one floating all over the internet. The only other piece of code really is the CreateKernel method is the global.asax, which looks like this:
protected override IKernel CreateKernel() {
IKernel kernel = new StandardKernel(
new NinjectSettings() { LoadExtensions = false },
new WcfNinjectModule(),
new DynamicProxy2Module()
);
return kernel;
}
Thanks for any help!
Matt
EDIT 12/12/2011: As requested, I've added some more detail below:
The entire wcf ninject module:
public class WcfNinjectModule : NinjectModule
{
public override void Load()
{
Bind<IMyDependency>().To<MyDependency>();
Bind<IService1>().To<Service1>();
}
}
The create kernel method in the global.asax is above, and the global.asax inherits from NinjectWcfApplication.
Service method looks like this:
public class Service1 : IService1
{
private IMyDependency _dependency;
public Service1()
{
}
public Service1(IMyDependency dependency)
{
_dependency = dependency;
}
[Time]
public virtual string GetData(string value)
{
return string.Format(_dependency.GetMyString(), value);
}
}
public interface IMyDependency
{
string GetMyString();
}
public class MyDependency : IMyDependency
{
[Time]
public virtual string GetMyString()
{
return "Hello {0}";
}
}
Does this help?
Since removing the 'WithConstructor' argument, the time intercept attribute will fire on GetMyString but not on GetData.
Matt
After a little more work (and writing that last post edit), it turns out that just removing the WithConstructorArgument method did resolve my problem and everything now seems to be working fine.
Matt