I'm migrating code from NSBv4 to NSBv5 (5.2.12 to be exact) and I have a custom profile implementation:
public class MyProfileHandler : IHandleProfile<PerformanceCounters>
{
public MyProfileHandler()
{
}
public void ProfileActivated(BusConfiguration config)
{
// I need to do something based on endpoint configuration, e.g. endpoint name
// this used to work in NSBv4:
// var endpointName = Configure.EndpointName;
}
}
How can I access endpoint configuration here?
I'm hosting this app using NServiceBus.Host (v6.0.0 if it matters) and this is where the IHandleProfile<T> interface comes from.
BusConfiguration is a configuration builder and it seems it's not possible to read anything useful from it. I tried to inject an instance of Configure to the constructor of my profile handler, but then it crashes - NSB needs the handler to have a parameterless constructor.
Implementing IWantTheEndpointConfig is not an option as well, as it is deprecated in v5 and it causes a compilation error. Its obsolete error message states:
IHandleProfile is now passed an instance of Configure
(which would be perfect for my case), but this is not true as far as I can tell (there is no Configure passed to ProfileActivated() and I can't see how I can inject it).
Is my only option to reimplement the profile handler using a completely different approach, or am I missing something?
NServiceBus.Core has an issue how it sets the endpoint name (and unfortunately also the endpoint version) on the BusConfiguration. The set endpoint name is added to the settings dictionary too late. You can work around that issue by doing the following:
public class EndpointConfig : IConfigureThisEndpoint
{
public void Customize(BusConfiguration configuration)
{
var customConfig = new EndpointConfiguration
{
EndpointName = "YourEndpointName",
};
configuration.EndpointName(customConfig.EndpointName);
configuration.GetSettings().Set<EndpointConfiguration>(customConfig);
}
}
public class EndpointConfiguration
{
public string EndpointName { get; set; }
}
BusConfiguration is essentially a dictionary on steroids. If you want to get access to what has been set in the BusConfiguration in the profile handler you can do the following (i.ex. get the endpoint name):
public class MyProfileHandler : IHandleProfile<PerformanceCounters>
{
public void ProfileActivated(BusConfiguration config)
{
var customConfig = config.GetSettings().Get<EndpointConfiguration>();
var endpointName = customConfig.EndpointName;
}
}
In the normal NServiceBus Host the interface offers only the one parameter, BusConfiguration. On Azure the interface offers two methods, where one actually has the Configure object.
Related
Struggling a little with Dependency Injection/Scoped Services with a rewrite rule class.
I have a redirects class which implements IRule
class ActivateRedirects : IRule
{
public void ApplyRule(RewriteContext context)
{
// Do stuff here which relies on CoreSettings (info below)
}
}
I also have a CoreSettings class which contains various settings, some of which are required for ApplyRule to work, it is initialised in startup.cs as follows.
var configurationBuilder = new ConfigurationBuilder();
configurationBuilder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "site.json"), optional: false, reloadOnChange: true);
IConfigurationRoot root = configurationBuilder.Build();
CoreSettings s = new CoreSettings();
services.Configure<CoreSettings>(root.GetSection("Website"));
So you can see above, CoreSettings is created as a Service, which in most cases I can consume with DI:
public class SomeOtherClass{
private CoreSettings Settings;
public SomeOtherClass(Microsoft.Extensions.Options.IOptionsSnapshot<CoreSettings> S)
{
Settings = S.Value;
}
// Do stuff with Settings ....
}
I have read up on several pages on why I can't simply add DI to the ActivateRedirects Class, or explicitly pass the value in using app.ApplicationServices.GetRequiredService but everything I have read is telling what I can't do, I can't find anything to tell me what I can!!
Before I am told to rewrite the code to not require CoreSettings for Rewriting, I can't do that because one of the rewrite rules depends on a condition which is set by a remote server via a REST API and what is needed in the CoreSettings class is the API credentials used to create the REST Client.
It is possible to do what you want. I'll assume your class is defined as follows:
public class ActivateRedirects : IRule
{
private readonly CoreSettings _coreSettings;
public ActivateRedirects(CoreSettings coreSettings)
{
_coreSettings = coreSettings;
}
public void ApplyRule(RewriteContext context)
{
}
}
Read the configuration file, as before:
services.Configure<CoreSettings>(root.GetSection("Website"));
Next, setup your RewriteOptions:
var coreSettings = app.ApplicationServices.GetRequiredService<IOptions<CoreSettings>>();
var activateRedirects = new ActivateRedirects(coreSettings.Value);
var rewriteOptions = new RewriteOptions();
rewriteOptions.Rules.Add(activateRedirects);
app.UseRewriter(rewriteOptions);
If you put a breakpoint inside ActivateRedirects, and send a request, you'll see the CoreSettings field has been populated.
Update
I think this scenario is what IOptionsMonitor<T> might be designed for. It's registered as a singleton, but is notified of options changes. Change ActivateRedirects to:
public class ActivateRedirects : IRule
{
private readonly IOptionsMonitor<CoreSettings> _coreSettings;
public ActivateRedirects(IOptionsMonitor<CoreSettings> coreSettings)
{
_coreSettings = coreSettings;
}
public void ApplyRule(RewriteContext context)
{
}
}
and change how the instance is constructed to:
var coreSettings = app.ApplicationServices.GetRequiredService<IOptionsMonitor<CoreSettings>>();
var activateRedirects = new ActivateRedirects(coreSettings);
For me, editing the configuration does now show updated values in CoreSettings. One caveat is I don't know how that notification process works. If this ends up reading the configuration directly on each request, then it will scale really poorly, so I'd advise giving it a good test first.
I've not worked with .Net Core before but have a lot of experience with MVC and Entity Framework. My project has four distinct folders, API, DTO, Repository and WEB. The DTO folder has many model files which fits the data model. The API folder has a Controller file called ReferenceDataController and looks like this
[Route("api/[controller]")]
[ApiController]
public class ReferenceDataController : ControllerBase
{
private readonly IReferenceDataRepository _repository;
public ReferenceDataController(IReferenceDataRepository repository)
{
_repository = repository;
}
// GET api/values
[HttpGet]
public ActionResult<ReferenceData> GetReferenceData()
{
return _repository.GetReferenceData();
}
I'm told that if I call this GET method it will return a data object. How do I call this method in the API folder from my HomeController in my WEB folder?
First, in your web project, you need to do a little setup. Add a class like the following:
public class ReferenceDataService
{
private readonly HttpClient _httpClient;
public ReferenceDataService(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public async Task<List<ReferenceData>> GetReferenceDataAsync(CancellationToken cancellationToken = default)
{
using (var response = await _httpClient.GetAsync("/api/referencedata", cancellationToken))
{
if (response.IsSuccessStatusCode())
{
return await response.Content.ReadAsAsync<List<ReferenceData>>();
}
return null;
}
}
}
Then, in ConfigureServices in Startup.cs:
services.AddHttpClient<ReferenceDataService>(c =>
{
c.BaseAddress = new Uri("https://api.example.com");
// Use the actual URL for your API here. You also probably want to get this
// from `Configuration` rather than hard-coding it.
});
Finally, inject ReferenceDataService into your HomeController:
public class HomeController : Controller
{
private readonly ReferenceDataService _referenceDataService;
public HomeController(ReferenceDataService referenceDataService)
{
_referenceDataService = referenceDataService ?? throw new ArgumentNullException(nameof(referenceDataService));
}
// In your action(s):
// var data = await _referenceDataService.GetReferenceDataAsync(HttpContext.RequestAborted);
}
This is the quick and dirty code here. Things you should consider for improvement:
Use an interface for your service class(es), i.e. IReferenceDataService. That will make testing easier. In ConfigureServices:
services.AddHttpClient<IReferenceDataService, ReferenceDataService>(...);
Then, inject IReferenceDataService instead.
You can and should use the Polly extensions with AddHttpClient to support retry and exception handling polices. At the very least, you'd definitely want to add AddTransientHttpErrorPolicy:
services.AddHttpClient<ReferenceDataService>(...)
.AddTransientHttpErrorPolicy(builder => builder.WaitAndRetryAsync(new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
}));
That will handle transient errors like temporarily being unable to connect to the API because it was restarted or something. You can find more info and more advanced configuration possibilities at the docs.
You should be using separate DTO classes. For brevity, I just used your (presumed) entity class ReferenceData. Instead, you should always use customized DTO classes that hold just the pieces of the data that you need to be available via the API. This way, you can control things like serialization as well as custom validation schemes, without conflicting with what's going on with your entity class. Additionally, the web project would only need to know about ReferenceDataDTO (or whatever), meaning you can share a library with your DTOs between the API and web projects and keep your DAL completely out of your web project.
TL;DR; some property is not being injected into a service when it should. How does Nservicebus handle nested containers on structuremap?
Hey guys new here. I'm currently upgrading our project to NSB6. I have a couple of question on dependency injection with Structuremap.
We have some services that use to use ISendOnlyBus, which we call from inside a handler. For test purposes(I just want to get the handler working) I changed those to IMessageHandlerContext, and at the beginning of the handler I'm injecting the context to the container. It works as expected, IMessageHandlerContext is resolved on these services and I'm able to use it.
public class MyMessageHandler : IHandleMessage<IMyMessage>
{
private IContainer _container;
private ISomeService _someService;
public MyMessageHandler (IContainer container, ISomeService someService)
{
_container = container;
_someService = someService;
}
public async Task Handle(IMyMessage message, IMessageHandlerContext context)
{
_container.Inject(typeof(IMessageHandlerContext), context);
_someService.DoSomething();
}
}
public class SomeService : ISomeService
{
private IMessageHandlerContext _context;
public SomeService(IMessageHandlerContext context)
{
_context = context;
}
public void DoSomething()
{
_context.Send<ISomeMessage>(x => {
//... init message
});
}
}
Now the issue is we have an Nhibernate custom IPostUpdateEventListener over an entity that sends nservicebus messages. On version 5 it had an ISendOnlyEndpoint that it used for sending, same as the other services, but this time the context is not being resolved by Structuremap. I'm wondering how's and if nservicebus manages nested containers inside message handlers. I cannot seem to find any documentation for it.
Child containers within StructureMap do not support some of the features required by NServiceBus containers. Policies are used to configure the injection, and policies are not supported in child containers.
Policies are only applied to root container in StructureMap
Here is an issue in the NServiceBus.StructureMap repo to track it.
I'm guessing the suggested refactor of this would be
public class MyMessageHandler : IHandleMessage<IMyMessage>
{
public async Task Handle(IMyMessage message, IMessageHandlerContext context)
{
context.Resolve<ISomeService>().DoSomething(context);
}
}
public class SomeService : ISomeService
{
public void DoSomething(IMessageHandlerContext context)
{
context.Send<ISomeMessage>(x => {
//... init message
});
}
}
context.Resolve would be be an extension you write to access the container instance in the context instance.
However if you wanted to just send messages from outside a message handler you can inject IEndpointInstance or IMessageSession which is returned when you start the bus.
I think they suggest to only send messages from message handlers using the context - but sometimes that just doesn't work.
I have got WCF service running as Windows service and I need to run a method of the WCF Service when Windows Service is starting. Is it possible in any way?
[ServiceContract]
public interface IWebMonitorServiceLibrary
{
[OperationContract]
void TestMethod();
}
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class WebMonitorServiceLibrary : IWebMonitorServiceLibrary
{
#region properties
#endregion
#region events
#endregion
public WebMonitorServiceLibrary()
{
Initialization();
}
private void Initialization()
{
/////////
}
public void TestMethod()
{
//////////
}
}
You don't explain why you want this initialization code to run, but given you almost never want to use a single-instance WCF service, the proper way would be to use dependency injection (see How do I pass values to the constructor on my wcf service?).
Create an object in which you store the things you want to initialize, which you initialize on your Windows Service start:
public class SomeSettingsYouWantToInitialize
{
public string SomeSetting { get; set; }
}
public class WindowsServiceInstance : ServiceBase
{
protected override void OnStart(string[] args)
{
InitializeWcfService();
}
private void InitializeWcfService()
{
var settings = new SomeSettingsYouWantToInitialize
{
SomeSetting = "Foo"
};
_yourDependencyContainer.Register<SomeSettingsYouWantToInitialize>(settings);
}
}
Then (using whatever dependency injection framework you use), inject that into your service's constructor:
public class WebMonitorServiceLibrary
{
public WebMonitorServiceLibrary(SomeSettingsYouWantToInitialize settings)
{
// do stuff with settings
}
}
Generally, no. This is because by default (and following best practice) you will have configured your service to run per-call (or per session), which means there can be multiple instances of your actual service running in your service host.
Therefore, any requirement for you to be able to return an instance of the service from the service host will involve some nasty plumbing code and is not advised.
Specifically, however, there are two approaches you could use to do what you want.
The first involves running your service in InstanceContextMode.Single - this means there will be a single service instance which will handle all requests. If you do this then you can simply create the service instance and then pass it into the servicehost when you start the windows service:
var service = new MyService();
var host = new ServiceHost(service);
You then have access to the service instance and can call the operation directly.
service.MyOperation("something");
The second thing you can do for when you don't want to run a singleton service you can make your service implementation just a wrapper around a static instance of a shared class that actually process the requests. As an example:
public class MyService : IMyService
{
private static IMyService instance = new MySharedServiceClass();
public static IMyService Instance
{
get { return instance ; }
}
public bool MyOperation(string something)
{
return instance.MyOperation(something);
}
}
Then you can call the method on the class like this:
var host = new ServiceHost(typeof(MyService));
var instance = MyService.Instance;
instance.MyOperation("something");
I would still avoid doing this if at all possible. Think to yourself why do you even want this method called on startup? Surely it would be better to have this code directly in the windows service if it's something that needs to be run on startup?
What I'm trying to do is the following:
1) I have the following WCF service contract:
[ServiceContract]
public interface IUploadService
{
[OperationContract]
ServiceData Upload(Request request);
}
[DataContract]
public class Request
{
[DataMember]
public long AbnNumber;
[DataMember]
public string Email;
}
2) This contract is implemented like this.
public class UploadService : IUploadService
{
public bool Upload(Request request)
{
// Some code
}
}
In the "Some code" section I would like to call a validation class to validate the clients request, so something like this:
var result = validation.ValidateRequest(request);
So my question is: Is it a bad idea to create an instance of my validation class inside the Upload method? Like this:
public class UploadService : IUploadService
{
public bool Upload(Request request)
{
var validation = new Validation();
var result = validation.ValidateRequest(request);
}
}
I know you can get around this by creating a constructor but as far as I know you can't create a constructor inside a WCF service implementation class, or am I wrong?
I'm new to WCF so if I'm totally heading the wrong direction please let me know.
Thanks
Personally I like as little as possible in my service methods. I would have a separate project to handle the Upload. This then allows you to reuse this code more easily, and to test the functionality without creating the service.
As to whether you should create your Validation like this it really depends on what it does, but generally I would make sure the Validation class implements an interface containing ValidateRequest(Request) and then inject that. You can then mock it in your tests if you need to.
So your service code would look like
public class UploadService : IUploadService
{
private readonly IUploadHandler _uploadHandler;
public UploadService(IUploadHandler uploadHandler)
{
_uploadHandler = uploadHandler;
}
public bool Upload(Request request)
{
//would possibly do some mapping here to create a different type of object to pass to the handler
_uploadHandler.Upload(request);
}
}
and the handler in a different project would look like
public class UploadHandler : IUploadHandler
{
private readonly IValidation _validator;
public UploadHandler(IValidation validator)
{
_validator = validator;
}
public bool Upload(Request request)
{
return _validator.ValidateRequest(request);
}
}
So my question is: Is it a bad idea to create an instance of my validation class inside the Upload method?
It comes down to whether you will be using Singleton or Per Call services. Usually it is better to have new instance of Service created for every request, and in that case it is OK to create all instances in your operation.
Interesting discussion on this topic Should WCF service typically be singleton or not?
If you decide to not to create Validation class for each then request there are two options:
Make it singleton
Create custom ServiceHostFactory for your service and initialize your Service in it (with constructor). Useful links on this topic:Extending Hosting Using ServiceHostFactory, Integrating StructureMap with WCF