Link event to dependency? - c#

I'm using TelemetryClient.TrackEvent to record file uploads within my .NET Core/C# web service, with their duration and throughput.
I'm also using DependencyTelemetry to connect together the various stages of my event handling:
using var dop = TelemetryClient.StartOperation<DependencyTelemetry>("Upload file event", correlationId);
However I don't see a way to connect the custom event to the dependency, so it shows up in the Application Insights Performance page. I see my request and the child dependencies, but no custom events. It would be great to see the custom events spawned from within my dependency, all linked together.
Is this possible?

Figured this out, and now I'm getting traces linked to requests/dependencies. Also showing how to set a custom role name on the TelemetryClient.
var tconfiguration = new TelemetryConfiguration
{
InstrumentationKey = Configuration.GetSection("ApplicationInsights")?["InstrumentationKey"]
};
tconfiguration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
this.TelemetryClient = new TelemetryClient(tconfiguration);
TelemetryClient.Context.Cloud.RoleName = Configuration.GetSection("ApplicationInsights")?["RoleName"];

Related

How to dynamically change OpenID configuration based on something in a single ASP.NET Core application?

We have some OpenID configuration specified in ConfigureServices in Startup.cs:
services.AddOpenIdConnect("something", "Something", options =>
{
// ... //
});
How can we change the configuration we've outlined here dynamically, on a per request basis, based on certain rules?
Can this be done in a middleware? If so, please give an example, thank you!
Don't think you can do that, but you can if you want add multiple services.AddOpenIdConnect(...) handlers and use a different one for different clients.
What kind of usecase do you have? What do you try to create?
There's nothing stopping you from adding the source of the OpenIdConnectHandler to your own application and then tweaking it to your needs. Its pretty simple and I have done that myself to learn the inner workings of it.
The source is here:
https://github.com/dotnet/aspnetcore/tree/master/src/Security/Authentication/OpenIdConnect
if (await _authenticationSchemeProvider.GetSchemeAsync(OpenIdConnectDefaults.AuthenticationScheme) == null)
_authenticationSchemeProvider.AddScheme(new AuthenticationScheme(OpenIdConnectDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme, typeof(OpenIdConnectHandler)));
var opendIDoption = new OpenIdConnectOptions();
OpenIDSSOConfiguration.SetupOIDOption(opendIDoption, openIDSSOConfiguration);
foreach(var postConfigure in _postConfigures)
{
postConfigure.PostConfigure(OpenIdConnectDefaults.AuthenticationScheme, opendIDoption);
}
_optionsCache.TryRemove(OpenIdConnectDefaults.AuthenticationScheme);
_optionsCache.TryAdd(OpenIdConnectDefaults.AuthenticationScheme, opendIDoption);
_postConfigureOptions.PostConfigure(OpenIdConnectDefaults.AuthenticationScheme, opendIDoption);
Here is my working solution of my.
serwices used are:
IEnumerable<IPostConfigureOptions<OpenIdConnectOptions>> postConfigures,
IPostConfigureOptions<OpenIdConnectOptions> postConfigureOptions,
IOptionsMonitorCache<OpenIdConnectOptions> optionsCache,
IAuthenticationSchemeProvider authenticationSchemeProvider)
Applying postConfigureOptions is crucial because OpenIdConnectHandler will throw exception that OpenIdConnectOptions is not correct configured.

How do I start a previously stopped Azure Container instance using C#?

I have the following code to stop an Azure container instance and would like to start it using similar.
using Microsoft.Azure.Management.Compute.Fluent.Models;
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal("XXXX", "XXXX", "XXXX", AzureEnvironment.AzureGlobalCloud);
var azure = Azure
.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(credentials)
.WithSubscription("XXXXX");
var containerName = "mycontainer";
var containerGroup = azure.ContainerGroups.GetByResourceGroup("myResourceGroup", containerName);
if (containerGroup.State == "Running")
{
containerGroup.Stop();
}
I would like to do the same and start my azure container instance. So where is containerGroup.Start(); ? This does not appear to exist in the interface. I have tried using containerGroup.Restart(); but this does not work from a stopped state. I need to be able to do this from within C# code and would like to avoid powershell if possible.
There is a way to do this but it is not exposed in the fluent API:
using Microsoft.Azure.Management.ContainerInstance.Fluent;
// azure is an instance of IAzure; the fluent Azure API
var resources = await azure.ContainerGroups.ListAsync();
foreach(var containerGroup in resources.Where(aci => aci.State != "Running"))
{
await ContainerGroupsOperationsExtensions.StartAsync(
containerGroup.Manager.Inner.ContainerGroups,
containerGroup.ResourceGroupName,
containerGroup.Name);
}
As mentioned by other people, you do need to realize that this is effectively starting a fresh container. No state will be maintained from the previous run unless you persisted that somewhere else like in a mounted volume.
You'll also need to grant the appropriate rights to whom ever is executing this code. I'm using a function so I had to setup a service account and a role, this blog post has all the details.
Update
The code I'm using is in on GitHub: https://github.com/alanta/azure_scheduler/blob/master/src/StartACIs.cs
Unfortunately, when you stop the container instances, they would be in the Terminated state and you cannot start them again.
Terminated or deleted container groups can't be updated. Once a
container group has stopped (is in the Terminated state) or has been
deleted, the group is deployed as new.
Even if you update the ACI, it also means the ACI would be redeployed. You can take a look at Update containers in Azure Container Instances. In addition, the Restart action also works when the container instances are in the running state.
So there is no start function in the C# SDK for you, at least now. Hope this will help you.
Update
Take a look at the event:
Each time when you start the container group after stop, the container group always these steps: pull the image -> create the container group -> start the container instances. So it’s clear, the container group was recreated when you start it after stop.

Autofac, best practices on runtime adding tenants?

I am using the multitenant container and each tenant has its own database + connectionstring registered in a InstancePerLifeTime scope. The tenant is identified using a subdomain which is mapped in a "master database" with a generated database name.
Now I have two use cases:
Use Case A: Creating new Tenants:
Someone fills in a registration form with the companyname, submits, and after submission we generate a new database and that tenant should be able to access the application under companyname.domain.com
However we want to do that without restarting the application which impacts all current tenants.
Let's say I want to add a new tenant, runtime. What is the best way to register this without restarting the application?
At first I thought about registering the container, inject it in my MVC Controller, and add the new registration runtime but after reading some questions this appears to be bad practice.
I could also get the DependencyResolver from within the Controller and access the container from there. Are there better practices available?
Use Case B: Register on demand
Assuming we have a big amount of tenants and want to prevent registering them all at once on application startup. We could register these in the multitenantcontainer on the first request when the subdomain can be matched to an existing account.
This might be premature optimization though, since basically we don't have lots of tenants yet.
But again, this would result in runtime registrations.
Container:
var tenantIdentificationStrategy= new TenantIdentificationStrategy();
var multitenantContainer = new MultitenantContainer(tenantIdentificationStrategy, builder.Build());
var tenants = new[]
{
"companyA.domain",
"localhost"
};
foreach (var id in tenants)
{
var databaseName = $"tenant-{id}";
multitenantContainer.ConfigureTenant(id, b =>
{
// Init RavenDB
b.Register(context => new RavenDocumentSessionFactory(databaseName))
.InstancePerTenant()
.AsSelf();
// Session per request
b.Register(context => context.Resolve<RavenDocumentSessionFactory>()
.FindOrCreate(context.Resolve<IDocumentStore>()))
.As<IDocumentSession>()
.InstancePerLifetimeScope()
.OnRelease(x =>
{
x.SaveChanges();
x.Dispose();
});
});
}
Your best bet is to hold a static reference to the application container somewhere and register your tenants from there. This is pretty common practice and, since your tenant registration code is going to have to "know" what a MultitenantContainer is anyway, it's not going to change your assembly references or spread the "knowledge" of the container around more than it would otherwise have to be.
Create the multitenant container at app startup.
Register the tenants you already know about.
Store the container in a static property somewhere that is globally accessible.
Reference the static property when you need to register a tenant.

How can I have a persistent data through different Context in a webservice?

So we have a webservice that is called from different applications and it runs an extraction of data which takes a while and we don't want it to run multiple times. So we thought we could set an HttpContext.Current.Application["isRunning"] to be persistent through all the requests like :
if ((bool)HttpContext.Current.Application["isRunning"])
And it doesn't work, since a new HttpContext is created when an other application call the webmethod.
Except writing onto the disk or in AppSettings I don't see how I can persist data through every request to only have one instance of my webmethod running at a time. I've tried with Application, Cache and static variables but they all do not persist across requests. It seems it creates a new instance each time.
Preventing a new instance to be created or persist data through instances would fix the issue. Any hint?
You could use EnterpriseLibraries Caching Block to cache the data following extraction.
http://www.codeproject.com/KB/web-cache/CachingApplicationBlock.aspx
Once you have the Enterprise Library assemblies referenced, it's just a case of adding a few lines to your web.config and then using code such as the following inside your service.
//Create Instance of CacheManager
ICacheManager _objCacheManager = CacheFactory.GetCacheManager();
AbsoluteTime timeToExpire = new AbsoluteTime(TimeSpan.FromMinutes(60));
MyData myData = null;
myData = (MyData)cacheManager.GetData("ref");
if (myData == null)
{
//get the data
cacheManager.Add("ref", myData, CacheItemPriority.Normal, null, timeToExpire);
}
return myData;
Take a look at the following links, which provide useful information on wanting to use the Singleton pattern with web services.
http://forums.asp.net/t/881617.aspx/1
http://social.msdn.microsoft.com/Forums/en-US/asmxandxml/thread/72274741-dbbe-4a64-a360-6bbe60026ec9/
http://msdn.microsoft.com/en-us/library/ff650316.aspx

How can you programatically add mappings to UnicastBusConfig?

I would like to be able to add subscriptions to additional message types living on potentially different servers to my application at runtime. What I'm trying to achieve is a Monitoring application where I can add/remove subscriptions at runtime. Is this possible? How do I get a reference to the current UnicastBus mappings?
Here is what I'm doing so far, but I believe this will overwrite any mappings currently in existence.
MessageEndpointMappingCollection mappings = new MessageEndpointMappingCollection();
mappings.Add(new MessageEndpointMapping()
{
Messages = m.MessageType.ToString(),
Endpoint = m.QueueName
});
IComponentConfig<UnicastBusConfig> busConfig = config.Configurer.ConfigureComponent<UnicastBusConfig>(ComponentCallModelEnum.None);
busConfig.ConfigureProperty(u => u.MessageEndpointMappings, mappings);
Yves used this code in his Azure samples (to be found in NSB samples collection)
using NServiceBus.Config;
using NServiceBus.Config.ConfigurationSource;
namespace OrderService
{
class ConfigOverride : IProvideConfiguration<UnicastBusConfig>
{
public UnicastBusConfig GetConfiguration()
{
return new UnicastBusConfig
{
MessageEndpointMappings = new MessageEndpointMappingCollection
{
new MessageEndpointMapping { Messages="MyMessages", Endpoint="orderserviceinputqueue" }
}
};
}
}
}
The best way to approach this would be to implement IConfigurationSource and provide your own configuration. Then you could cherry pick what you would like to load from the config file (if anything) and what you would like to specify yourself at runtime.
I would reflect the DefaultConfigurationSource class or refer to this gist for guidance.
In a project, I am currently involved with, we are doing some content-based routing to dynamically subscribed/unsubscribed agents by keeping track of them in our own routing table.
We have wrapped the IBus in a decorator (by using Windsor's support of decorators, as described <plug>here</plug>), that sees if the message implements a special IRoutableMessage interface, that allows the decorator to route the message by explicitly specifying the destination endpoint via bus.Send(destinationEndpoint).
This was a little bit complex to get right, and I would recommend going with NServiceBus' built-in routing as far as possible. But is is possible to explicitly route messages to any endpoint.
If you are looking into monitoring, check out the NSBManager repository. This takes the opposite approach and lets the endpoints register with the manager.

Categories

Resources