I am using NServicebus(version 4.6.3) with SQLTransport in my ASP.net web api project. I have different connectionstrings for the queues for different environments (Dev,QA,etc). My configuration looks like below:
public class BusConfigurator
{
public static IStartableBus Bus { get; private set; }
public static void DisposeBus()
{
if (Bus == null)
return;
Bus.Shutdown();
Bus.Dispose();
Bus = null;
}
public static void InitializeServiceBus(string connectionString)
{
var configure = Configure.With()
.DefineEndpointName("MyEndPoint")
.Log4Net(new DebugAppender { Threshold = Level.Warn })
.UseTransport<SqlServer>(connectionString)
.PurgeOnStartup(false)
.SetDefaultTransactionLevel()
.UnicastBus(); // Error is thrown here on second call
configure.MyCustomSQLServerPersistence();
Bus = configure.CreateBus();
}
public static void StartBus()
{
Bus.Start(() => Configure.Instance.ForInstallationOn<NServiceBus.Installation.Environments.Windows>().Install());
}
}
I have a dropdown in the app so that the user can select the environment. Based on the selection, I want to reconfigure the bus. So, I call DisposeBus then pass the connection string to the IntializeServiceBus method followed by the startBus. It works first time but throws error below when it gets called again with different connectionstring:
Unable to set the value for key: NServiceBus.Transport.ConnectionString. The settings has been locked for modifications. Please move any configuration code earlier in the configuration pipeline
Source=NServiceBus.Core
Line=0
BareMessage=Unable to set the value for key: NServiceBus.Transport.ConnectionString. The settings has been locked for modifications. Please move any configuration code earlier in the configuration pipeline
Is NServicebus intended to be used/configured this way? (I am guessing probably not) If not then is there a workaround/different approach for this?
In V4 or below, there is no way to do it by normal human means. There is only one Bus per AppDomain. All of the configuration API is static, so if you try, you get exactly the problems you ran into.
By "human means", I mean that it might be possible to do something crazy with spinning up a new AppDomain within your process, setting up a Bus within that, and then tearing it down when you're finished. It might be possible. I haven't tried it. I wouldn't recommend it.
In V5, the configuration API is completely redesigned, is not static, and so this is possible:
var cfg = new BusConfiguration();
// Set up all the settings with the new V5 Configuration API
using (var justOneBus = NServiceBus.Bus.Create(cfg).Start())
{
// Use justOneBus, then it gets disposed when done.
}
That's right. It's disposable. Then you can do it again. In your case you wouldn't want to put it in a using block - you would want to set it up somewhere, and when the dropdown gets switched, call Dispose on the current instance and rebuild it with the new parameters.
Keep in mind, however, that the Bus is still pretty expensive to create. It's definitely still something you want to treat as an application-wide singleton (or singleton-like) instance. You definitely wouldn't want to spin up a separate one per web request.
Related
I am trying to Force the immediate instantiation or a Singleton in .Net 6. The reason I want to do this is because I want to load some data the moment that the application starts. I don't know if my approach is correct and if it is possible. Suggestions are more than welcome.
This is what I have tried so far.
My program.cs configuration
builder.Services.AddScoped<IIpLocationService, IpLocationService>();
var serviceProvider = builder.Services.BuildServiceProvider();
var memoryCacheProvider = new MemoryCacheProvider(
serviceProvider.CreateScope().ServiceProvider
.GetService<IIpLocationService>());
builder.Services.AddSingleton<IMemoryCacheProvider>(memoryCacheProvider)
But I get this error.
System.InvalidOperationException: 'Unable to resolve service for type 'Auth.In_MemoryDatabase.IMemoryCacheProvider' while attempting to activate 'Auth.Services.IpLocationService'.'
IIpLocationService has its own dependencies too. Namely a unitofwork in charge of retrieving the information from a database and the MemoryCacheProvider class to store the information in-memory.
This is my code of the MemoryCacheProvider
public interface IMemoryCacheProvider
{
List<IpLocation> IpLocationList { get; set; }
}
public class MemoryCacheProvider : IMemoryCacheProvider
{
private readonly IIpLocationService _ipLocationService;
public List<IpLocation> IpLocationList { get; set; } = new List<IpLocation>();
public MemoryCacheProvider(IIpLocationService ipLocationService)
{
_ipLocationService = ipLocationService;
Init();
}
private void Init()
{
// do something with it
_ipLocationService.Init();
}
}
Is there a way I can make this work or something similar? I just need the data to start loading the moment the application starts because it's a lot of data. For this reason, I must be able to call the Init method of the IpLocationService.
I am not quite sure what you are trying to accomplish
Is there a way I can make this work or something similar? I just need the data to start loading the moment the application starts because it's a lot of data. For this reason, I must be able to call the Init method of the IpLocationService.
Maybe you can take a look in a Hosted service (IHostedService). This code will run before requests can be received. This way you can inject your dependancies in the hosted service, and fetch fetch your data.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio#startasync
Alternatively, you can run it in your Program.cs
You can get your services after building the Host, but before you run it.
var host = CreateHostBuilder(args).Build();
host.Services.GetRequiredService<...>(...);
host.Run();
I hope this gives you some ideas on how to solve your issue.
This is a practice ASP.NET project I'm using to better understand a few techniques, and while I've got Dependency Injection working, its not working quite as I want it to. I have a class that I want to use to store a history, so every time the user hits a submit button, it displays a result, and after the second time it starts displaying the history. Anyway I added the history to the DI as a scoped service, thinking that would mean it would be created and then remain the same instance for the duration of the session for that user. However according to the debugger it looks like the list never gets bigger than one, and thats at the point of adding the item to the list. So the code.
The object
{
public class RollHistory : IRollHistory
{
public List<IRollMessage> Entries { get; set; } = new List<IRollMessage>();
}
}
The DI
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddTransient<IDiceTray, DiceTray>();
services.AddTransient<IRollMessage, RollMessage>();
services.AddScoped<IRollHistory, RollHistory>();
}
The Controller constructor
public HomeController(ILogger<HomeController> logger, IDiceTray diceTray, IRollMessage rollMessage, IRollHistory rollHistory)
{
_logger = logger;
_diceTray = diceTray;
_rollMessage = rollMessage;
_rollHistory = rollHistory;
}
And the code for when the button gets clicked
[HttpPost]
public IActionResult Index(DiceRollModel diceRoll)
{
_diceTray.DiceRoll(diceRoll.DiceType, diceRoll.DiceCount, diceRoll.Bonus, diceRoll.VantageType);
_rollMessage.RollMessages(_diceTray);
diceRoll.RollResult = _rollMessage;
_rollHistory.Entries.Add(_rollMessage);
diceRoll.History = _rollHistory.Entries;
return View(diceRoll);
}
It's worth noting I've tried to code this at least 4 different ways with and without DI, the only way it works is if I use AddSingleton, while this might not be an issue because this app is unlikely to ever be live, its a poor excuse not to do it right.
I believe “scope” is by default per request which would explain that each submit gets is own service.
“Doing stuff right” is of course to some extend a matter of opinion. But my opinion would clearly be that I would avoid server-side session to avoid problems with scaling to more than one instance. There are also ways to support shared state, but this is difficult. To me singletons are not a code smell either, but they have their own problems.
Your problem might be solved by storing whatever state you need in the browser either in a cookie or localStorage. Your service would then have request scope, but it would read user state from browser causing “user scope” for the data. (But don’t rely on browser state to persist and remember it is modifiable to the user.)
Details
I have attempted to create a background processing structure using the recommended IHostedService interface in ASP.NET 2.1. I register the services as follows:
services.AddSingleton<AbstractProcessQueue<AbstractImportProcess>>();
services.AddHostedService<AbstractBackgroundProcessService<AbstractImportProcess>>();
services.AddSignalR();
The AbstractProcessQueue is just a wrapper around a BlockingCollection of processes that can be enqueued and dequeued. The AbstractBackgroundProcessService implements the IHostedService interface and looks at the queue for new processes it can start.
Now, the trouble starts when, inside a SignalR hub, I attempt to get a reference to the background processing service via the Dependency Injection mechanisms. I have tried the following solutions, but none seem to be working as intended:
Option 1:
public HubImportClient(IServiceProvider provider)
{
//This returns null.
var service = provider.GetService<AbstractBackgroundProcessService<AbstractImportProcess>>();
}
Option 2:
public HubImportClient(IServiceProvider provider)
{
//This returns null.
var service = (AbstractBackgroundProcessService<AbstractImportProcess>) provider.GetService(typeof(AbstractBackgroundProcessService<AbstractImportProcess>>));
}
Option 3:
public HubImportClient(IServiceProvider provider)
{
//This throws an exception, because the service is missing.
var service = provider.GetRequiredService<AbstractBackgroundProcessService<AbstractImportProcess>>();
}
Option 4:
public HubImportClient(IServiceProvider provider)
{
//This throws an exception, because the service is missing.
var service = (AbstractBackgroundProcessService<AbstractImportProcess>) provider.GetRequiredService(typeof(AbstractBackgroundProcessService<AbstractImportProcess>);
}
Option 5:
public HubImportClient(IServiceProvider provider)
{
//This returns a correct service, but prevents me from adding additional AbstractBackgroundProcessService implementations with different type parameters.
//Additionally, it seems like this reference was newly created, and not the instance that was created on application startup (i.e. the hash codes are different, and the constructor is called an additional time).
var service = provider.GetService<IHostedService>();
if(service is AbstractBackgroundProcessService<AbstractProcessService>)
{ this.Service = (AbstractBackgroundProcessService<AbstractProcessService>) service;}
}
Option 6:
public HubImportClient(IServiceProvider provider)
{
//This works similarly to the previous option, and allows multiple implementations, but the constructor is still called twice and the instances thus differ.
AbstractBackgroundProcessService<AbstractImportProcess> service = null;
foreach(IHostedService service in provider.GetServices<IHostedService>())
{
if(service is AbstractBackgroundProcessService<AbstractImportProcess>)
{
service = (AbstractBackgroundProcessService<AbstractImportProcess>) service;
break;
}
}
}
Option 7:
public HubImportClient(IServiceProvider provider)
{
//This just skips the for each loop all together, because no such services could be found.
AbstractBackgroundProcessService<AbstractImportProcess> service = null;
foreach(AbstractBackgroundProcessService<AbstractImportProcess> current in provider.GetServices<AbstractBackgroundProcessService<AbstractImportProcess> >())
{
service = current;
break;
}
}
Option 8:
//This works, but prevents multiple implementations again.
public HubImportClient(IHostedService service)
{
this.Service = service;
}
Option 9:
//This does not work again.
public HubImportClient(AbstractBackgroundProcessService<AbstractImportProcess> service)
{
this.Service = service;
}
Question
So then my question remains: how am I supposed to get a reference to an IHostedService implementation so that:
(a): I can inject multiple instances of the service that differ only by their type parameter (e.g. a hosted service for AbstractImportProcesses as well as one for AbstractExportProcesses)
(b): there is only ever one instance of the IHostedService for that specific type parameter.
Thanks in advance for any help!
Current workaround from mentioned git page:
services.AddSingleton<YourServiceType>();
services.AddSingleton<IHostedService>(p => p.GetRequiredService<YourServiceType>());
Or, if your service implements some other interfaces:
services.AddSingleton<YourServiceType>();
services.AddSingleton<IYourServiceType>(p => p.GetRequiredService<YourServiceType>());
services.AddSingleton<IHostedService>(p => p.GetRequiredService<YourServiceType>());
This creates your service as hosted (runs and stops at host's start and shutdown), as well as gets injected as depedency wherever you require it to be.
Update:
I don't see this solution as a "workaround" anymore.
Instead, I would describe it this way: hosted component and a regular service are entities of different types, each one serving its own purpose.
The solution above, however, allows one to combine them, registering a hosted component as a service, which can be used in the dependency resolution chain.
Which is awesome.
This is just a slight modification to the answer by #AgentFire. This method is clearer and allows for several background hosted services in a single Web Service.
services.AddSingleton<YourServiceType>();
services.AddHostedService<YourServiceType>(p => p.GetRequiredService<YourServiceType>());
There has been some discussion around this topic. For example, see: https://github.com/aspnet/Hosting/issues/1489. One of the problems that you'll run into is that hosted services are added as transient services (from ASP.NET Core 2.1+), meaning that resolving an hosted service from the dependency injection container will result in a new instance each time.
The general advice is to encapsulate any business logic that you want to share with or interact from other services into a specific service. Looking at your code I suggest you implement the business logic in the AbstractProcessQueue<AbstractImportProcess> class and make executing the business logic the only concern of AbstractBackgroundProcessService<T>.
In .Net Core 3.1 and .Net 5.0you can get references to the existing instances of the Hosted Services with:
IEnumerable<IHostedService> allHostedServices = this.serviceProvider.GetService<IEnumerable<IHostedService>>();
You get this directly from the IServiceProvider in dotnet core 3.1 and later:
var myHostedService = serviceProvider
.GetServices<IHostedService>()
.OfType<MyHostedService>()
.Single();
Is it possible to disable Application Insights dependency tracking for a specific method/function?
My specific problem is that I've got a custom ITelemetryInitializer and within that, I'm calling a static function that can have an external dependency if the value isn't already cached in memory. Within that function, it's creating a new ITelemetry and calling my ITelemetryInitializer, which generates a new ITelemetry, etc., resulting in a StackOverflowException.
Basically (this is NOT my actual code):
public void Initialize(ITelemetry telemetry)
{
var cached = GetCachedValue();
if(cached)
return cached;
else
{
var value = GetData(); // New Telemetry gets created here - since it's not cached yet, the new telemetry gets initialized and goes right back here.
SetCachedValue(value);
}
}
I'm reworking my initializer to NOT use the external dependency, but I think the question is still valid - if there's a specific thing I don't want tracked, can I disable telemetry for that thing (in my example, I'd want to turn off tracking for the GetData method)?
It is possible to disable dependency tracking using ITelemetryProcessor (https://learn.microsoft.com/en-us/azure/application-insights/app-insights-api-filtering-sampling).
But this will not help in your case since ITelemetryInitializer will be called first anyway. The right solution is not to do dependency calls from ITelemetryInitializer.
So I've decided to up the performance a bit in my WCF application, and attempt to cache Channels and the ChannelFactory. There's two questions I have about all of this that I need to clear up before I get started.
1) Should the ChannelFactory be implemented as a singleton?
2) I'm kind of unsure about how to cache/reuse individual channels. Do you have any examples of how to do this you can share?
It's probably important to note that my WCF service is being deployed as a stand alone application, with only one endpoint.
EDIT:
Thank you for the responses. I still have a few questions though...
1)I guess I'm confused as to where the caching should occur. I'm delivering a client API that uses this code to another department in our company. Does this caching occur on the client?
2)The client API will be used as part of a Silverlight application, does this change anything? In particular, what caching mechanisms are available in such a scenario?
3)I'm still not clear about the design of the GetChannelFactory method. If I have only one service, should only one ChannelFactory ever be created and cached?
I still haven't implemented any caching feature (because I'm utterly confused about how it should be done!), but here's what I have for the client proxy so far:
namespace MyCompany.MyProject.Proxies
{
static readonly ChannelFactory<IMyService> channelFactory =
new ChannelFactory<IMyService>("IMyService");
public Response DoSomething(Request request)
{
var channel = channelFactory.CreateChannel();
try
{
Response response = channel.DoSomethingWithService(request);
((ICommunicationObject)channel).Close();
return response;
}
catch(Exception exception)
{
((ICommenicationObject)channel).Abort();
}
}
}
Use the ChannelFactory to create an instance of the factory, then cache that instance. You can then create communicatino channels as needed/desired from the cached istance.
Do you have a need for multiple channel factories (i.e.., are there multiple services)? In my experience, that's where you'll see the biggest benefit in performance. Creating a channel is a fairly inexpensive task; it's setting everything up at the start that takes time.
I would not cache individual channels - I'd create them, use them for an operation, and then close them. If you cache them, they may time out and the channel will fault, then you'll have to abort it and create a new one anyway.
Not sure why you'd want to usea singleton to implement ChannelFactory, especially if you're going to create it and cache it, and there's only one endpoint.
I'll post some example code later when I have a bit more time.
UPDATE: Code Examples
Here is an example of how I implemented this for a project at work. I used ChannelFactory<T>, as the application I was developing is an n-tier app with several services, and more will be added. The goal was to have a simple way to create a client once per life of the application, and then create communication channels as needed. The basics of the idea are not mine (I got it from an article on the web), though I modified the implementation for my needs.
I have a static helper class in my application, and within that class I have a dictionary and a method to create communication channels from the channelf factory.
The dictionary is as follows (object is the value as it will contain different channel factories, one for each service). I put "Cache" in the example as sort of a placeholder - replace the syntax with whatever caching mechanism you're using.
public static Dictionary<string, object> OpenChannels
{
get
{
if (Cache["OpenChannels"] == null)
{
Cache["OpenChannels"] = new Dictionary<string, object>();
}
return (Dictionary<string, object>)Cache["OpenChannels"];
}
set
{
Cache["OpenChannels"] = value;
}
}
Next is a method to create a communication channel from the factory instance. The method checks to see if the factory exists first - if it does not, it creates it, puts it in the dictionary and then generates the channel. Otherwise it simply generates a channel from the cached instance of the factory.
public static T GetFactoryChannel<T>(string address)
{
string key = typeof(T.Name);
if (!OpenChannels.ContainsKey(key))
{
ChannelFactory<T> factory = new ChannelFactory<T>();
factory.Endpoint.Address = new EndpointAddress(new System.Uri(address));
factory.Endpoint.Binding = new BasicHttpBinding();
OpenChannels.Add(key, factory);
}
T channel = ((ChannelFactory<T>)OpenChannels[key]).CreateChannel();
((IClientChannel)channel).Open();
return channel;
}
I've stripped this example down some from what I use at work. There's a lot you can do in this method - you can handle multiple bindings, assign credentials for authentication, etc. Its pretty much your one stop shopping center for generating a client.
Finally, when I use it in the application, I generally create a channel, do my business, and close it (or abort it if need be). For example:
IMyServiceContract client;
try
{
client = Helper.GetFactoryChannel<IMyServiceContract>("http://myserviceaddress");
client.DoSomething();
// This is another helper method that will safely close the channel,
// handling any exceptions that may occurr trying to close.
// Shouldn't be any, but it doesn't hurt.
Helper.CloseChannel(client);
}
catch (Exception ex)
{
// Something went wrong; need to abort the channel
// I also do logging of some sort here
Helper.AbortChannel(client);
}
Hopefully the above examples will give you something to go on. I've been using something similar to this for about a year now in a production environment and it's worked very well. 99% of any problems we've encountered have usually been related to something outside the application (either external clients or data sources not under our direct control).
Let me know if anything isn't clear or you have further questions.
You could always just make your ChannelFactory static for each WCF Contract...
You should be aware that from .Net 3.5 the proxy objects are pooled for performance reasons by the channel factory. Calling the ICommunicationObject.Close() method actually returns the object to the pool in the hope it can be reused.
I would look at the profiler if you want to do some optimisation, if you can prevent just one IO call being made in your code it could far outweigh any optimisation you will make with the channel factory. Don't pick an area to optimise, use the profiler to find where you can target an optimisation. If you have an SQL database for instance, you will probably find some low hanging fruit in your queries that will get you orders of magnitude performance increases if these haven't already been optimised.
Creating the Channel costs the performance so much. actually , WCF already has the cache mechanism for the ChannelFactory if you use the ClientBase in the client instead of the pure ChannelFactory. But the cache will be expired if you make some anditional operations(Please google it for details if you want).
For the ErOx's issue i got another solution i think it is better. see below:
namespace ChannelFactoryCacheDemo
{
public static class ChannelFactoryInitiator
{
private static Hashtable channelFactories = new Hashtable();
public static ChannelFactory Initiate(string endpointName)
{
ChannelFactory channelFactory = null;
if (channelFactories.ContainsKey(endpointName))//already cached, get from the table
{
channelFactory = channelFactories[endpointName] as ChannelFactory;
}
else // not cached, create and cache then
{
channelFactory = new ChannelFactory(endpointName);
lock (channelFactories.SyncRoot)
{
channelFactories[endpointName] = channelFactory;
}
}
return channelFactory;
}
}
class AppWhereUseTheChannel
{
static void Main(string[] args)
{
ChannelFactory channelFactory = ChannelFactoryInitiator.Initiate("MyEndpoint");
}
}
interface IMyContract { }
}
you can customize the logic and the parameters of the Initiate method yourself if you got another requirement. but this initiator class is not limited only one endpoint. it is powerful for all of the endpoint in your application. hopefully. it works well for you. BTW. this solution is not from me. i got this from a book.