I've got an Azure function which I'm adding Application Insights logging to.
It uses autofac to manage the dependencies in a config file, and I have added as follows:
private static void RegisterDependencies(ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<ApplicationInsightsLoggerFactory>()
.As<ILoggerFactory>()
.SingleInstance();
containerBuilder.Register(client => new TelemetryClient(new TelemetryConfiguration("[instrumentation key]"))).As<TelemetryClient>().SingleInstance();
...
In Program.cs 'Main', this class is called like this IocConfig.Configure()
The function runs with no errors, but the logging is not appearing in Application Insights. Have I missed something in this configuration?
You don't to write the DI yourself to get a TelemetryClient. The built-in DI is already available for you to just get one:
public class MyFunctionClass
{
private readonly TelemetryClient telemetryClient;
/// Using dependency injection will guarantee that you use the same configuration for telemetry collected automatically and manually.
public MyFunctionClass(TelemetryConfiguration telemetryConfiguration)
{
this.telemetryClient = new TelemetryClient(telemetryConfiguration);
}
Related
I'm working on a solution that interacts with Redis, using the servicestack.net library.
I have a class that inherits from ServiceStack.AppHostBase and asks me for an override of the Configure method. This method has as a parameter a Funq.Container that I see is an implementation of IServiceProvider, IResolver and IContainer, and none of these interfaces have the AddHttpClient method that is provided by the IServiceCollection. Method I need to be able to inject the IHttpClientFactory. Any idea how to solve my problem?
To do it in ASP.NET (not .NET Core), the quick way would be to:
install Microsoft.Extensions.DependencyInjection package and call .AppHttpClient() extension
Build the Service Provider you would normally see in .NET Core
Get the instance of IHttpClientFactory from the Service Provider
Register the instance of IHttpClientFactory with Funq.Container again
using Microsoft.Extensions.DependencyInjection;
public class AppHost : AppHostBase
{
public override void Configure(Container container)
{
...
RegisterHttpClientFactory(container);
}
private container RegisterHttpClientFactory(Container container)
{
var services = new ServiceCollection()
.AddHttpClient();
// You can kind of inspect services returned.
// You can see this extension registers lot of other things too beside
// IHttpClientFactory.
// Also you can see the lifetime of IHttpClientFactory is Singleton.
var serviceProvider = services.BuildServiceProvider();
container.AddSingleton(serviceProvider.GetService<IHttpClientFactory>());
return container;
}
}
If you happen to use Unity Adaptor
Unity has a package to give you an extension as well to build the Service Provider directly into the Unity Container:
using Microsoft.Extensions.DependencyInjection;
using Unity;
using Unity.Microsoft.DependencyInjection;
public static class UnityConfig
{
public static void RegisterTypes(IUnityContainer container)
{
...
container.RegisterServices();
container.RegisterHttpClientFactory();
}
private static IUnityContainer RegisterHttpClientFactory(
this IUnityContainer unityContainer)
{
new ServiceCollection()
.AddHttpClient()
.BuildServiceProvider(unityContainer);
return unityContainer;
}
}
This is the interface definition of IServiceCollection from IServiceCollection.cs:
public interface IServiceCollection : IList<ServiceDescriptor>
{
}
AddHttpClient is just an extension method from Microsoft.Extensions.DependencyInjection that wraps adding a number of additional dependencies to ASP.NET Core IOC.
So you should continue to register it on ASP.NET Core IOC, i.e:
public class Startup : ModularStartup
{
public new void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseServiceStack(new AppHost
{
AppSettings = new NetCoreAppSettings(Configuration)
});
}
}
As any dependencies registered .NET Core Startup are also available to ServiceStack.
I want to create an Azure Durable Function that will download a CSV from the Internet and based on the data in this file, it will update my database using EntityFramework.
I set up the simple startup function that is triggered with TimeTrigger. This function is responsible for starting the orchestrator. The orchestrator executes multiple activities in parallel. There are around 40000 work items to be processed, so that's the number of activities that are triggered by orchestrator. Some of these activities will need to update the database (insert/update/delete rows). For this I need a database connection. I can configure DI in the StartUp in the following way:
public override void Configure(IFunctionsHostBuilder builder)
{
var connectionString = Environment.GetEnvironmentVariable("DefaultConnection");
builder.Services.AddDbContext<SqlContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddScoped<IDbContext, SqlContext>();
}
}
However all my functions (orchestrator, activity function, etc.) are static and reside in a static class. I haven't seen any example where durable functions were defined in a non-static class and I had all kinds of problems when I tried that myself, so I assumed they must be static without diving too much into it.
I do not know how to pass my DbContext object to the Activity function, so it can update the data in the database when needed.
How should I resolve it?
I want to create an Azure Durable Function that will download a CSV from the Internet and based on the data in this file, it will update my database using EntityFramework.
Configure DI in the StartUp in the following way:
public override void Configure(IFunctionsHostBuilder builder) {
var connectionString = Environment.GetEnvironmentVariable("DefaultConnection");
builder.Services.AddDbContext<IDbContext, SqlContext>(options =>
options.UseSqlServer(connectionString)); //To inject DbContext
builder.Services.AddHttpClient(); //To inject HttpClient
}
Ensure you host your function app on Azure Functions Runtime V3+ so the class and methods don’t have to be static.
This will allow regular classes that have non-static constructors with injectable arguments
public class MyFunction {
private readonly HttpClient httpClient;
private readonly IDbContext dbContext;
//ctor
public MyFunction(IHttpClientFactory factory, IDbContext dbContext) {
httpClient = factory.CreateClient();
this.dbContext = dbContext;
}
[FunctionName("Function_Name_Here")]
public async Task Run(
[OrchestrationTrigger] IDurableOrchestrationContext context) {
// ... access dependencies here
}
// ... other functions, which can include static, but they wont
// have access to the instance fields.
}
This series of articles might be of some assistance to you
A Practical Guide to Azure Durable Functions — Part 2: Dependency Injection
With AspNetCore.SignalR (1.0.0 preview1-final) and AspNetCore.All (2.0.6), how can I invoke a method on a hub in server code that is not directly in a Controller and is in a class that cannot be made via Dependency Injection?
Most examples assume the server code is in a Controller and should 'ask' for the hub via an injectable parameter in a class that will created by DI.
I want to be able to call the hub's method from server code at any time, in code that is not injected. The old SignalR had a GlobalHost that enabled this approach. Basically, I need the hub to be a global singleton.
Now, everything seems to be dependent on using Dependency Injection, which is introducing a dependency that I don't want!
I've seen this request voiced in a number of places, but haven't found a working solution.
Edit
To be more clear, all I need is to be able to later access the hubs that I've registered in the Configure routine of the Startup class:
app.UseSignalR(routes =>
{
routes.MapHub<PublicHubCore>("/public");
routes.MapHub<AnalyzeHubCore>("/analyze");
routes.MapHub<ImportHubCore>("/import");
routes.MapHub<MainHubCore>("/main");
routes.MapHub<FrontDeskHubCore>("/frontdesk");
routes.MapHub<RollCallHubCore>("/rollcall");
// etc.
// etc.
});
If I register them like this:
services.AddSingleton<IPublicHub, PublicHubCore>();
it doesn't work, since I get back an uninitiated Hub.
No It's not possible. See "official" answer from david fowler https://github.com/aspnet/SignalR/issues/1831#issuecomment-378285819
How to inject your hubContext:
Best solution is to inject your hubcontext like IHubContext<TheHubWhichYouNeedThere> hubcontext
into the constructor.
See for more details:
Call SignalR Core Hub method from Controller
Thanks to those who helped with this. Here's what I've ended up on for now...
In my project, I can call something like this from anywhere:
Startup.GetService<IMyHubHelper>().SendOutAlert(2);
To make this work, I have these extra lines in Startup.cs to give me easy access to the dependency injection service provider (unrelated to SignalR):
public static IServiceProvider ServiceProvider { get; private set; }
public static T GetService<T>() { return ServiceProvider.GetRequiredService<T>(); }
public void Configure(IServiceProvider serviceProvider){
ServiceProvider = serviceProvider;
}
The normal SignalR setup calls for:
public void Configure(IApplicationBuilder app){
// merge with existing Configure routine
app.UseSignalR(routes =>
{
routes.MapHub<MyHub>("/myHub");
});
}
I don't want all my code to have to invoke the raw SignalR methods directly so I make a helper class for each. I register that helper in the DI container:
public void ConfigureServices(IServiceCollection services){
services.AddSingleton<IMyHubHelper, MyHubHelper>();
}
Here's how I made the MyHub set of classes:
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
public class MyHub : Hub { }
public interface IMyHubHelper
{
void SendOutAlert(int alertNumber);
}
public class MyHubHelper : IMyHubHelper
{
public IHubContext<MyHub> HubContext { get; }
public MyHubHelper(IHubContext<MyHub> hubContext)
{
HubContext = hubContext;
}
public void SendOutAlert(int alertNumber)
{
// do anything you want to do here, this is just an example
var msg = Startup.GetService<IAlertGenerator>(alertNumber)
HubContext.Clients.All.SendAsync("serverAlert", alertNumber, msg);
}
}
This is a nice solution. In .NET Core 2.1 the service provider is disposed and you get cannot access disposed object. The fix is to create a scope:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider.CreateScope().ServiceProvider;
I develop a ASP.NET Core application working with Azure Tables.
So, I created a tables storage account in Azure Portal, created a table, filled it with some test data, and now I would like to display the content of that table to test the reading.
my appsettings.json is
{
"ConnectionStrings": {
"MyTables":"DefaultEndpointsProtocol=https;AccountName=yyy;AccountKey=xxx;EndpointSuffix=core.windows.net"
},
"Logging": {
"IncludeScopes": false,
[etc etc...]
}
}
And my Startup.cs:
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
// here in debug we can see the connection string, that is OK
Console.WriteLine($"conn string:{Configuration["ConnectionStrings:MyTables"]}");
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
}
And here is my controller I try to Display the values:
using Microsoft.AspNetCore.Mvc;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using NextMove.Models;
using System.Text;
[...]
public class HelloWorldController : Controller
{
public string ReadTables() {
// ????? Code does not work, as Startup not a reference
string myConnString = Startup.Configuration["ConnectionStrings:MyTables"];
//////////////////////////////////
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(myConnString);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("themes");
TableQuery<ProjectThemeEntity> query = new TableQuery<ProjectThemeEntity>().Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "fr"));
StringBuilder response = new StringBuilder("Here is your test table:");
foreach (ProjectThemeEntity item in table.ExecuteQuery(query)) {
response.AppendLine($"Key: {item.RowKey}; Value: {item.Description}");
}
return response.ToString();
}
//
// GET: /HelloWorld/
public IActionResult Index() {
return View();
}
Questions:
a) How to fix this code in order to get the connection string?
b) There should be a "Table.ExecuteQuery(query)" as per this MSDN article in the controller's foreach, but it does not find such a method in CloudTable class, I however added the necessary references, as shown in the controller's code above, only two "Async" methods are available:
PS.
-For the (b) question several people has the same issue here, hope the situation changed now...
You can't access Startup.Configuration from the controller because it's not a static property. Even though you've made it public (generally not a good idea) it still requires you to have an instance of Startup to get access to it.
Generally to get access to settings in ASP.NET Core it's best to create a class with the properties you want and use the IOptions pattern to get them with Dependency Injection. In your startup where you configure your services (add services to the dependency injection container) you would use the helper methods to add your configuration object to the container and then in your controller you would specify you wanted an IOptions or IOptionsSnapshot to get access to it.
I'd suggest you don't put your data access in your controller though. It makes your controller harder to read and harder to maintain if you need to change your strategy later. Move your ReadTables method to its own class and add it to the DI container in Startup taking whatever settings you need to create the service. Use constructor injection in your controller to get the service and execute calls from your controller actions where you need them.
I have an OWIN pipeline using Nancy:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseNancy();
}
}
The UseNancy() is actually a call to my own custom extension method defined in this gist: https://gist.github.com/TheFastCat/0b7635d9e5795b44e72e
This code is executed both as an Azure Website or an Azure Cloud Service. Based on the context it is executing within I want to use a particular favicon, loaded as an embedded resource from a separate assembly. I do this by specifying separate NancyBootstrappers (each loading the proper favicon for its context).
Is there a more elegant solution to determining the runtime application that is executing the OWIN pipeline? Currently I check app.Properties["host.AppName"] ; however while the Website's app name matches it's assembly configuration, the CloudService app is the name of the Owin startup assembly.class. (see gist). It's cloogey.
Is there a more elegant/simple solution for specifying a custom favicon within Nancy for each of my web applications than creating separate bootstrappers and doing runtime application context checks?
I solved this problem with the help of others on the https://jabbr.net/#/rooms/owin and https://jabbr.net/#/rooms/nancyfx chat boards
Yes. You can contextually check the OWIN host properties:
if (app.Properties.ContainsKey("System.Net.HttpListener"))
{
// self hosted application context
}
2.) Yes.
namespace ClassLib
{
public class Startup()
{
public Startup(byte[] favIcon) { ... }
public void Configuration(IAppBuilder app) { ... }
}
}
[assembly: OwinStartup(typeof(WebHost.Startup))]
namespace WebHost
{
public class Startup()
{
public voic Configuration(IAppBuilder app)
{
new ClassLib.Startup(webhostFavIcon).Configuration(app);
}
}
}
namespace SelfHost
{
private class Program()
{
public void Main(string[] args)
{
using(WebApp.Start(app => new ClassLib.Startup(selfHostFavIcon).Configuration(app))
{}
}
}
}