Having successfully created and run a simple Azure Durable Function in Visual Studio 2017, I want to introduce logging.
The Visual Studio project template generates the static HttpStart class with a
Run method containing an optional parameter of type Microsoft.Extensions.Logging.ILogger.
I have no idea how to hook up dependency injection into a durable function project. Can anyone point me to an example on how to achieve this?
It looks to me like I will need some class, inside which I will need to use the Microsoft.Extensions.Logging.LoggingFactory.CreateLogger() method.
I imagine that this logic will need to be within a container class that is hooked into the run time pipeline using HostBuilder in some way (similar to using WebHostBuilder in static main method).
Thank you
#Thomas pointed me to examples I used to extract code that allows dependency injection to work when writing a durable function project:
using System.IO;
using System.Reflection;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Extensions.Logging;
using tmetadastoreFnApp;
using Willezone.Azure.WebJobs.Extensions.DependencyInjection;
using ILogger = Microsoft.Extensions.Logging.ILogger;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
[assembly: WebJobsStartup(typeof(Startup))]
namespace tmetadastoreFnApp
{
internal class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder) =>
builder.AddDependencyInjection(ConfigureServices);
private void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ILoggerFactory, LoggerFactory>();
services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
services.AddLogging((builder) => builder.SetMinimumLevel(LogLevel.Trace));
var serviceProvider = services.BuildServiceProvider();
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties = true });
var dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
LogManager.LoadConfiguration(Directory.GetParent(dir) + "\\nlog.config");
}
}
}
Related
I am using Castle Windsor v3.4.0 to create a RavenDB document session instance but when I use a RavenDB client version later than 3.0.3660 I get this error when calling the Store method:
Castle.MicroKernel.ComponentNotFoundException: 'No component for supporting the service System.Net.Http.HttpMessageHandler was found'
Here is the smallest piece code I can come up with that reproduces the error:
using Castle.Facilities.TypedFactory;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Raven.Client;
using Raven.Client.Document;
public class Program
{
public static void Main()
{
var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();
container.Register(
Component
.For<IDocumentStore>()
.ImplementedBy<DocumentStore>()
.DependsOn(new { Url = "http://localhost:8081", DefaultDatabase = "Test" })
.OnCreate(x => x.Initialize())
.LifeStyle.Singleton,
Component
.For<IDocumentSession>()
.UsingFactoryMethod(x => x.Resolve<IDocumentStore>().OpenSession())
.LifeStyle.Transient);
using (var documentSession = container.Resolve<IDocumentSession>())
{
documentSession.Store(new object());
documentSession.SaveChanges();
}
}
}
Here's what I believe is happening. A change was made to the RavenDB client after v3.0.3660 that changed how the HttpMessageHandler is created in the HttpJsonRequest class:
https://github.com/ravendb/ravendb/commit/740ad10d42d50b1eff0fc89d1a6894fd57578984
I believe that this change in combination with my use of the TypedFactoryFacility in my Windsor container is causing RavenDB to request an instance of HttpJsonRequestFactory and it's dependencies from Windsor rather than using it's own internal one.
How I can change my code to avoid this problem so that I can use a more recent version of the RavenDB client?
Given your MVCE, Windsor is set up to inject object's properties. So, when creating the DocumentStore, Castle is trying to find a value for the HttpMessageHandlerFactory property and is failing since nothing is configured for that particular type.
I was able to get your example to work (at least, it got to inserting the data into my non-existing server) by just filtering out that property:
container.Register(
Component.For<IDocumentStore>()
.ImplementedBy<DocumentStore>()
.DependsOn(new { Url = "http://localhost:8081", DefaultDatabase = "Test" })
.OnCreate(x => x.Initialize())
.PropertiesIgnore(p => p.Name == nameof(DocumentStore.HttpMessageHandlerFactory))
.LifeStyle.Singleton);
Alternatively, if you have a value for it, you could add it to the object passed to DependsOn().
With Asp.Net it was easy to see the code generated by the Razor View Engine: Add a compile error and the error page will give access to the source of the Razor Page.
This changed with Asp.Net Core, which I read somewhere creates the code in memory and does not allow access to that code easily.
Question: Someone knows a trick how to access generated Razor source code with Asp.Net Core?
Add the following class to your ASP.NET Core MVC project:
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
public class CustomCompilationService : DefaultRoslynCompilationService, ICompilationService
{
public CustomCompilationService(ApplicationPartManager partManager,
IOptions<RazorViewEngineOptions> optionsAccessor,
IRazorViewEngineFileProviderAccessor fileProviderAccessor,
ILoggerFactory loggerFactory)
: base(partManager, optionsAccessor, fileProviderAccessor, loggerFactory)
{
}
CompilationResult ICompilationService.Compile(RelativeFileInfo fileInfo,
string compilationContent)
{
return base.Compile(fileInfo, compilationContent);
}
}
Override the ICompilationService added by MVC with the above class;
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<ICompilationService, CustomCompilationService>();
}
Set a break point on the Compile method of CustomCompilationService and view compilationContent.
Notes
View lookups are case sensitive. If your controller routing seeks a view named Index (Index.cshtml) but you've named your view file index (index.cshtml), you'll receive an exception:
InvalidOperationException: The view 'Index' was not found.
Artificial Stupidity provided the correct answer for ASP.NET core 1.x. For version 2.0 of the framework, one can instead use a custom razor template engine:
using Microsoft.AspNetCore.Mvc.Razor.Extensions;
using Microsoft.AspNetCore.Razor.Language;
public class CustomMvcRazorTemplateEngine : MvcRazorTemplateEngine
{
public CustomMvcRazorTemplateEngine(RazorEngine engine, RazorProject project) : base(engine, project)
{ }
public override RazorCSharpDocument GenerateCode(RazorCodeDocument codeDocument)
{
RazorCSharpDocument razorCSharpDocument = base.GenerateCode(codeDocument);
// Set breakpoint here for inspecting the generated C# code in razorCSharpDocument.GeneratedCode
// The razor code can be inspected in the Autos or Locals window in codeDocument.Source._innerSourceDocument._content
return razorCSharpDocument;
}
}
Then override the RazorTemplateEngine of the framework:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<RazorTemplateEngine, CustomMvcRazorTemplateEngine>();
}
In version 2.1 of ASP.NET Core, the RazorTemplateEngine seems to be legacy, and the above mechanism does not work anymore. The changes may have to do with the move towards precompilation of razor views, but since I am not involved in the development, I can only guess at the developers' motives.
I would now recommend inspecting the precompiled views in the **.Views.dll generated at build or publish time, depending on your project settings. I personally use Telerik's JustDecompile for this purpose.
If you really need to have a programmatic solution, you can hook into the RazorProjectEngine with a custom phase:
using Microsoft.AspNetCore.Razor.Language;
namespace Econet.PAG.UI
{
internal class DebugRazorEnginePhase : IRazorEnginePhase
{
public RazorEngine Engine { get; set; }
public void Execute(RazorCodeDocument codeDocument)
{
RazorCSharpDocument razorCSharpDocument = codeDocument.GetCSharpDocument();
// Set breakpoint here for inspecting the generated C# code in razorCSharpDocument.GeneratedCode
// The razor code can be inspected in the Autos or Locals window in codeDocument.Source._innerSourceDocument._content
}
}
}
and register it in the creation of the RazorProjectEngine
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton(s =>
{
var fileSystem = s.GetRequiredService<RazorProjectFileSystem>();
var projectEngine = RazorProjectEngine.Create(RazorConfiguration.Default, fileSystem, builder =>
{
RazorExtensions.Register(builder);
// Roslyn + TagHelpers infrastructure
var metadataReferenceFeature = s.GetRequiredService<LazyMetadataReferenceFeature>();
builder.Features.Add(metadataReferenceFeature);
builder.Features.Add(new CompilationTagHelperFeature());
// TagHelperDescriptorProviders (actually do tag helper discovery)
builder.Features.Add(new DefaultTagHelperDescriptorProvider());
builder.Features.Add(new ViewComponentTagHelperDescriptorProvider());
builder.Phases.Add(new DebugRazorEnginePhase());
});
}
Note that except for the line adding the custom phase, the code inside AddSingleton is copied from Microsoft.Extensions.DependencyInjection.MvcRazorMvcCoreBuilderExtensions.AddRazorViewEngineServices(IServiceCollection services) in the Microsoft.AspNetCore.Mvc.Razor sources.
In a simple Console Application:
using Microsoft.AspNetCore.Razor.Language;
class Program
{
static void Main(string[] args)
{
var sourceDocument = RazorSourceDocument.Create("Hello world", "");
var codeDocument = RazorCodeDocument.Create(sourceDocument);
var engine = RazorEngine.Create();
engine.Process(codeDocument);
var csharpDocument = codeDocument.GetCSharpDocument();
var csharp = csharpDocument.GeneratedCode;
Console.WriteLine(csharp);
}
}
Output is:
#pragma checksum "" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "7b502c3a1f48c8609ae212cdfb639dee39673f5e"
// <auto-generated/>
#pragma warning disable 1591
namespace Razor
{
#line hidden
public class Template
{
#pragma warning disable 1998
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
WriteLiteral("Hello world");
}
#pragma warning restore 1998
}
}
#pragma warning restore 1591
I'm working on a existing Windows Phone project and want to use the IOC container from MVVMCross, but not the other extra features (yet).
I installed MVVMCross.Core 4.x and try to use 'ConstructAndRegisterSingleton' from the App() constructor of the Windows app, but it throws an Null ref exception.
Tried to find any bootstrapper, setup or initialization for MVVMCross but can't find any in the new 4.x core.
Anyone any idea?
Found it.... and it seems to work.
Just get MVVMCross.Core from Nuget and create a setup like:
internal static class Setup
{
public static void InitializeIoc()
{
CreateIocProvider();
// Register all services
Mvx.ConstructAndRegisterSingleton<ILoudnessLimitsRegulator, LoudnessLimitsRegulator>();
}
private static void CreateIocProvider()
{
// Ioc options
var options = new MvxIocOptions();
// initialize the IoC registry, then add it to itself
var iocProvider = MvxSimpleIoCContainer.Initialize(options);
Mvx.RegisterSingleton(iocProvider);
}
}
I have an SSIS package which has a script task. It has of course the usual Script main class file, it uses a couple of web services so I used the WSDL command line too to generate a couple of proxy classes for those in the project. All works fine.
I defined a couple of read-only User:: variables to be used by the script task which define the URL paths of the two web services.
Problem : in the two web service proxy classes I seem not to have access to the Dts variables collection. I've tried adding the using Microsoft.SqlServer.Dts.Runtime; to each of the proxy classes but still cannot access those Dts variables within the proxy classes.
Is there a way to do this?
I was able to pass the variables to a class constructor by passing the ScriptObjectModel.
// in the ScriptMain class
YourWsClass ws = new YourWsClass(Dts);
//in the WS class
using Microsoft.SqlServer.Dts.Tasks.ScriptTask;
class YourWsClass
{
public YourWsClass(ScriptObjectModel dts)
{
string _v = (string)dts.Variables["User::YourVariable"].Value;
}
}
You won't be able to access the variables directly anywhere else than in the ScriptMain class. You have to pass the variable to your proxy class when you instantiate it.
// in the ScriptMain class
YourWsClass ws = new YourWsClass(Dts.Variables["User::YourVariable"].Value.ToString();
class YourWsClass
{
public YourWsClass(String v)
{
_v = v;
}
}
I have some code that's using a custom filter provider to perform property injection using Windsor on my action filters in a WebAPI project.
I'm just upgrading it from WebAPI beta to WebAPI RC, and I have a method in my container registration that used to read:
container.Register(
Component
.For<IEnumerable<IFilterProvider>>()
.UsingFactoryMethod(
() => GlobalConfiguration.Configuration.ServiceResolver.GetFilterProviders()
)
);
This would allow me to get the 'default' collection of filter providers and pass that into my custom filter provider, which would in turn retrieve the filters from each default provider, inject any missing dependencies via property injection, and return the filter with dependencies resolved.
In ASP.NET WebAPI RC, the ServiceResolver is now DependencyResolver and no longer has a GetFilterProviders() method - what should I be using in its place?
EDIT: Right, the following registration syntax is working - as in, it's passing all the unit tests and doesn't appear to be leaking memory or anything - but I'm not sure that explicitly binding to the instance returned by the Services.FilterProviders() is a good idea...
var filterProviders = GlobalConfiguration.Configuration.Services.GetFilterProviders();
container.Register(
Component.For<IEnumerable<IFilterProvider>>().Instance(filterProviders),
Component.For<IFilterProvider>().ImplementedBy<WindsorFilterProvider>()
);
and, for the sake of completeness, the WindsorFilterProvider looks like this:
public class WindsorFilterProvider : IFilterProvider {
private readonly IWindsorContainer container;
private readonly IEnumerable<IFilterProvider> filterProviders;
public WindsorFilterProvider(IWindsorContainer container, IEnumerable<IFilterProvider> filterProviders) {
this.container = container;
this.filterProviders = filterProviders;
}
public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor) {
var filters = filterProviders.SelectMany(fp => fp.GetFilters(configuration, actionDescriptor)).ToList();
foreach (var filter in filters) container.Inject(filter.Instance);
return filters;
}
}
I guess the question is - is this a good way of doing this, or is there a recommended approach that I should be using instead?