Can I determine `IsDevelopment` from `IWebJobsBuilder` - asp.net-core

Very much an XY problem, but I'm interested in the underlying answer too.
See bottom for XY context.
I'm in a .NET Core 3 AzureFunctions (v3) App project.
This code makes my question fairly clear, I think:
namespace MyProj.Functions
{
internal class CustomStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
var isDevelopment = true; //Can I correctly populate this, such that it's true only for local Dev?
if(isDevelopment)
{
// Do stuff I wouldn't want to do in Prod, or on CI...
}
}
}
}
XY Context:
I have set up Swagger/Swashbuckle for my Function, and ideally I want it to auto-open the swagger page when I start the Function, locally.
On an API project this is trivial to do in Project Properties, but a Functions csproj doesn't have the option to start a web page "onDebug"; that whole page of project Properties is greyed out.
The above is the context in which I'm calling builder.AddSwashBuckle(Assembly.GetExecutingAssembly()); and I've added a call to Diagnostics.Process to start a webpage during Startup. This works just fine for me.
I've currently got that behind a [Conditional("DEBUG")] flag, but I'd like it to be more constrained if possible. Definitely open to other solutions, but I haven't been able to find any so ...

While I am not completely sure that it is possible in azure functions I think that setting the ASPNETCORE_ENVIRONMENT application setting as described in https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings should allow you to get whether the environment is set as production or development by injecting a IHostEnvironment dependency and checking
.IsDevelopment()
on the injected dependency.

Related

Is there an option in Serilog to change log file parameters in runtime, the same way LogLevel can be changed?

With Serilog in asp.net core you can change the log level in runtine by using
MinimumLevel.ControlledBy(SeriLogLevelSwitch).
Is there a similar way to do this with LoggerConfiguration().WriteTo.File(...)
For instance i need to change the configuration for log fileSizeLimitBytes, or rollingInterval withour restaring the service. Can this be achieved with Serilog?
By pulling in the latest Serilog.AspNetCore you'll find a class called ReloadableLogger, constructed through the CreateBootstrapLogger() extension method:
// using Serilog;
var logger = new LoggerConfiguration()
.WriteTo.File(...)
.CreateBootstrapLogger();
// Optional but suggested:
Log.Logger = logger;
// Use the logger...
// Change parameters later on:
logger.Reload(lc => lc
.WriteTo.File(...));
You might find that some interactions between CreateBootstrapLogger() and UseSerilog(callback) in ASP.NET Core trip things up a bit; if you use this technique, try the parameterless version of UseSerilog().
ReloadableLogger has only just appeared and is focusing on a slightly different scenario, so you may still need to work through some awkwardness setting this up - YMMV.

How to use Miniprofiler storage to support multiple web instances?

I've hooked up Miniprofiler to my local ASP.NET Core project and it works as expected. Now I need it to work in a hosted environment where there are multiple instances of the same website and there are no sticky sessions. It is my understanding that this should be supported if you just set the storage option when configuring the profiler. However, setting the storage does not seem to do anything. I initialize the storage like this:
var redisConnection = "...";
MiniProfiler.DefaultOptions.Storage = new RedisStorage(redisConnection);
app.UseMiniProfiler();
After doing this, I expected that I could open a profiled page and a result would be added to my redis cache. I would then also expect that a new instance of my website would list the original profiling result. However, nothing is written to the cache when generating new profile results.
To test the connection, I tried manually saving a profiler instance (storage.Save()) and it gets saved to the storage. But again, the saved result is not loaded when showing profiler results (and regardless, none of the examples I've seen requires you to do this). I have a feeling that I've missed some point about how the storage is supposed to work.
It turns out that my assumption that MiniProfiler.DefaultOptions.Storage would be used was wrong. After changing my setup code to the following, it works.
// Startup.cs ConfigureServices
var redisConnection = "...";
services.AddMiniProfiler(o =>
{
o.RouteBasePath = "/profiler";
o.Storage = new RedisStorage(redisConnection); // This is new
});
// Startup.cs Configure
app.UseMiniProfiler();

UseLegacyPathHandling is not loaded properly from app.config runtime element

I'm trying to use the new long path support at my app. In order to use it, without forcing clients to have the newest .NET 4.6.2 version instelled on their machines, one should only add those elements to his app.config (see the link for more info https://blogs.msdn.microsoft.com/dotnet/2016/08/02/announcing-net-framework-4-6-2/) :
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/>
</startup>
<runtime>
<AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false" />
</runtime>
When I use it in my execution project it works perfectly. The problem is in my testing projects (which use Nunit). I've added app.config to my test project in the same way I've added it to the execution project.
Using the ConfigurationManager class I've managed to ensure that app config indeed loaded (in short: using an app setting which i was able to retrieve in a unit test).
Using ConfigurationManager.GetSection("runtime"), I even managed to ensure the runtime element has been loaded properly (_rawXml value is the same as in app.config).
But (!) for some reason the app config runtime element is not influencing the UseLegacyPathHandling variable and therefore all of my calls with long path fail.
I guess the problem is somehow relates to the fact that testing projects become dll's that are loaded using the Nunit engine, which is the execution entry point.
I'm facing the exact same problem in another project I have, which is a dll loaded by Office Word application. I believe the problem is the same in both cases and derived from the fact that the projects are not meant to be an execution entry point.
It's important to understand that I've no access to the executions their self (Word Office or Nunit) and therefore I can't configure them myself.
Is there an option to somehow make the AppContextSwitchOverrides get loaded from scratch dynamically? Other ideas will be most welcome.
I've been having the same issue, and have noted the same lack of loading of that particular setting.
So far, what I've got is that the caching of settings is at least partly to blame.
If you check out how it's implemented, disabling the cache has no effect on future calls to values (i.e. if caching is enabled and something is accessed during that time, then it will always be cached).
https://referencesource.microsoft.com/#mscorlib/system/AppContext/AppContext.cs
This doesn't seem to be an issue for most of the settings, but for some reason the UseLegacyPathHandling and BlockLongPaths settings are getting cached by the time I can first step into the code.
At this time, I don't have a good answer, but if you need something in the interim, I've got a highly suspect temporary fix for you. Using reflection, you can fix the setting in the assembly init. It writes to private variables by name, and uses the specific value of 0 to invalidate the cache, so it's a very delicate fix and not appropriate for a long term solution.
That being said, if you need something that 'just works' for the time being, you can check the settings, and apply the hack as needed.
Here's a simple example code. This would be a method you'll need in your test class.
[AssemblyInitialize]
public static void AssemblyInit(TestContext context)
{
// Check to see if we're using legacy paths
bool stillUsingLegacyPaths;
if (AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out stillUsingLegacyPaths) && stillUsingLegacyPaths)
{
// Here's where we trash the private cached field to get this to ACTUALLY work.
var switchType = Type.GetType("System.AppContextSwitches"); // <- internal class, bad idea.
if (switchType != null)
{
AppContext.SetSwitch("Switch.System.IO.UseLegacyPathHandling", false); // <- Should disable legacy path handling
// Get the private field that is used for caching the path handling value (bad idea).
var legacyField = switchType.GetField("_useLegacyPathHandling", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
legacyField?.SetValue(null, (Int32)0); // <- caching uses 0 to indicate no value, -1 for false, 1 for true.
// Ensure the value is set. This changes the backing field, but we're stuck with the cached value for now.
AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out stillUsingLegacyPaths);
TestAssert.False(stillUsingLegacyPaths, "Testing will fail if we are using legacy path handling.");
}
}
}

Export to pdf using ASP.NET 5

I am working on MVC 6 application(DNX Core 5.0 framework). Unfortunately, I don't find any library for pdf export.
Any help will be appreciated.
I finally figured out a way to generate pdf's from .NET Core (without any .NET framework dependencies) is using Node.js from within my .NET Core application.
The following example shows how to implementing a HTML to PDF converter in a clean ASP.NET Core Web Application project (Web API template).
Install the NuGet package Microsoft.AspNetCore.NodeServices
In Startup.cs add the line services.AddNodeServices() like this
public void ConfigureServices(IServiceCollection services)
{
// ... all your existing configuration is here ...
// Enable Node Services
services.AddNodeServices();
}
Now install the required Node.js packages:
From the command line change working directory to the root of the .NET Core project and run these commands.
npm init
and follow the instructions to create the package.json file
npm install jsreport-core --save
npm install jsreport-jsrender --save
npm install jsreport-phantom-pdf --save
Create a file pdf.js in the root of the project containing
module.exports = function (callback) {
var jsreport = require('jsreport-core')();
jsreport.init().then(function () {
return jsreport.render({
template: {
content: '<h1>Hello {{:foo}}</h1>',
engine: 'jsrender',
recipe: 'phantom-pdf'
},
data: {
foo: "world"
}
}).then(function (resp) {
callback(/* error */ null, resp.content.toJSON().data);
});
}).catch(function (e) {
callback(/* error */ e, null);
})
};
Have a look here for more explanation on jsreport-core.
Now create an action in an Mvc controller that calls this Node.js script
[HttpGet]
public async Task<IActionResult> MyAction([FromServices] INodeServices nodeServices)
{
var result = await nodeServices.InvokeAsync<byte[]>("./pdf");
HttpContext.Response.ContentType = "application/pdf";
string filename = #"report.pdf";
HttpContext.Response.Headers.Add("x-filename", filename);
HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "x-filename");
HttpContext.Response.Body.Write(result, 0, result.Length);
return new ContentResult();
}
Off course you can do whatever you want with the byte[] returned from nodeServices, in this example I'm just outputting it from a controller action so it can be viewed in the browser.
You could also exchange the data between Node.js and .NET Core by a base64 encoded string using resp.content.toString('base64') in pdf.js and use
var result = await nodeServices.InvokeAsync<byte[]>("./pdf"); in the action and then decode the base64 encoded string.
Alternatives
Most pdf generator solutions still depend on .NET 4.5/4.6 framework.
None of the two answers above (JsReport and RazorPDFCore) works for .NET Core yet.
There seems to be some paid alternatives available if you don't like to use Node.js:
NReco.PdfGenerator.LT
EVO HTML to PDF Converter Client for .NET Core
Winnovative HTML to PDF Converter Client for .NET Core
I haven't tried any of these though.
I hope we will soon see some open source progress in this area.
If you must rely on Core you'll have two options:
1 - Wait a bit
Core is still RC1, slowly moving to RC2, and you won't find much libs really soon. Since .NET Core is taking much attention, first libs should come out in a few months, but I'd guess you'll have to wait for at least RC2 release.
2 - Fork (or similar)
You can grab an open-source project that best fits your needs, fork (if on GitHub) or just download and start updating to .NET Core. I've just done that with DapperExtensions and it's working like a charm. You can even add some spicy just for you ;)
On the other hand, if you just need something that works but with no direct need of embedding into .NET Core, I've managed to make JsReport work fine. It will start it's very own server (embedded server) based on Node but integration is really easy (with AspNet Core very own Dependecy Injection system!) and PDF are created with no further issue.
If that interests you, here are some instructions:
1 - References
Add those to your project.json:
"jsreport.Embedded": "0.8.1",
"jsreport.Client": "0.8.1"
2 - AspNet integration
After, follow instructions from jsReport here. You can configure AspNet DI system as here:
public void ConfigureServices(IServiceCollection services)
{
// ...
var _server = new EmbeddedReportingServer();
_server.StartAsync().Wait();
services.AddInstance<IEmbeddedReportingServer>(_server);
services.AddSingleton<IReportingService>((s) => { return s.GetRequiredService<IEmbeddedReportingServer>().ReportingService; });
// ...
}
To use you'll just have to either receive an IReportingService or manually grab it from Resolver on your controller, for instance.
3 - Usage
public IActionResult SomeReport()
{
// This is <my> type of usage. It's a bit manual because I'm currently loading reports from DB. You can use it in a diferent way (check jsReport docs).
var service = Resolver.GetRequiredService<jsreport.Client.IReportingService>();
var phantomOptions = new jsreport.Client.Entities.Phantom()
{
format = "A4",
orientation = "portrait",
margin = "0cm"
};
phantomOptions.footer = "<h2>Some footer</h2>";
phantomOptions.footerHeight = "50px";
phantomOptions.header = "<h2>Some header</h2>";
phantomOptions.headerHeight = "50px";
var request = new jsreport.Client.RenderRequest()
{
template = new jsreport.Client.Entities.Template()
{
content = "<div>Some content for your report</div>",
recipe = "phantom-pdf",
name = "Your report name",
phantom = phantomOptions
}
};
var _report = service.RenderAsync(request).Result;
// Request file download.
return File(_report.Content, "application/pdf", "Some fancy name.pdf");
}
4 - Important: your server won't start (missing a zip file)
Due to changes from NuGet on AspNet projects, you have to manually move some content files which are not moved automatically.
First, find your dnx cache for the embedded server. Should be something like:
C:\Users\<name>\.dnx\packages\jsreport.Embedded\0.8.1.
You'll notice a folder called content there. Simply copy it's contents (two files: node.exe and jsreport-net-embedded.zip) into lib\net45.
So, to be plain simple and fool-proof: copy contents (files only) from
C:\Users\<name>\.dnx\packages\jsreport.Embedded\0.8.1\contents
into
C:\Users\<name>\.dnx\packages\jsreport.Embedded\0.8.1\lib\net45.
That should solve startup issues. Remember: first startup will extract files and should take a few minutes. After that, it will be much much faster.
I know that this question was asked a while ago, and I know that there have been several answers provided already that may well be right for certain projects. But I recently created a GitHub repository that allows for the creation of PDFs directly from your C# code without any requirement for nodejs, javascript, or razor. The feature set is a bit limited at the moment but it generates PDFs with images (.jpg only at this stage), shapes, and formatted text. The library works with .net core 2.0 and has no dependency on any other PDF generation tool.
Please note that this is my own repository: https://github.com/GZidar/CorePDF
I do plan to add functionality over time but at least for now this may provide the basis for others to include simple PDF capability in their own projects without the need for additional tooling.
I have amended the RazorAnt/RazorPDF which was working only for older MVC versions to work with ASP.NET Core. Its RazorPDFCore, available on nuget and github:
Example usage
class YourBaseController : RazorPDF.Controller {
// [...]
public IActionResult Pdf() {
var model = /* any model you wish */
return ViewPdf(model);
}
}
In your Startup.cs
add the following line before services.AddMVc();
services.AddSingleton<PdfResultExecutor>();
PLEASE NOTE:
You need to inhere RazorPDF.Controller from your base controller before using the ViewPdf() method
Necromancing.
Adding a dependency to NodeJS is subideal IMHO, especially considering .NET Core self-contained deployment.
As per 2017, you could use my port of PdfSharpCore to .NET Core 1.1
Resolves fonts, and it can use images. Comes with a nice sample application. You'll have to replace the DB part, however.
Credits go to:
https://github.com/groege/PdfSharpCore
which is a bit outdated, and doesn't contain a sample on how to use it with images.
Note that you need to register the font-resolver and the imageSource-Implementation before using the respective features:
PdfSharpCore.Fonts.GlobalFontSettings.FontResolver = new FontResolver();
MigraDocCore.DocumentObjectModel.MigraDoc.DocumentObjectModel
.Shapes.ImageSource.ImageSourceImpl =
new PdfSharpCore.ImageSharp.ImageSharpImageSource();

Conversion of V2 Ninject Binding to V3

I've been banging my head at this for about 8 hours now, and I just can't seem to find a simple explanation on how to change my custom bootstrapper for ninject (Last worked on the code back in v2.x.x.x) to the new v3.0.0.0 syntax.
I currently have the following:
public class NinjectCustomBootStrapper : NinjectNancyBootstrapper
{
protected override Ninject.IKernel GetApplicationContainer()
{
return Program.MyContainer;
}
}
in a septate class, and :
public static IKernel MyContainer
{
get { return _myContainer ?? (_myContainer = CreateKernel()); }
set { _myContainer = value; }
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<CardMonitorService>().ToSelf().InSingletonScope();
return kernel;
}
in my main program 'Program.c' in a command line app.
Iv'e since updated ninject to V3.0.0.0 only to find that there's been some breaking changes. I'll admit I don't use ninject very often (I usually use structuremap), and the only reason this project does is I didn't write it originally.
Since I've upgraded Ninject, now when the app is started up I get the following exception:
Method not found: 'Ninject.Syntax.IBindingWhenInNamedWithOrOnSyntax`1<!0>
Ninject.Syntax.IBindingToSyntax`1.ToConstant(!0)'.
After a ton of searching and researching, the closest I've been able to find is this:
http://sharpfellows.com/post/Ninject-Auto-registration-is-changing-in-version-3.aspx
Which while it points me in the right direction, still isn't quite a solution as I'm not using a custom binding generator.
So my question is this.
How do I rewrite the above so that my project once again works and the WCF service when called gets the correct singleton binding handed to it when a request comes in. Going back to ninject 2 is not an option, as other dependencies in the project that have been added have forced the v3 upgrade and these add new functionality that's been requested hence why I'm working on it.
For reference this is a .NET4 build, running on NancyFX with a self hosting WCF setup as a windows service using Topshelf to provide the SCM interface.
Cheers
Shawty
Addendum to clear things up a little
This is an existing project that was originally written sometime back, I've been asked to add some new features to the project.
As part of adding these new features I have been required to upgrade the version of Ninject being used from an earlier version to V3.0.0.0 as newer dependencies added to the project require the newer version of Ninject.
Under the previous V2 of Ninject the code I have given above worked fine, with no issues, since the project has had Ninject V3 added I now get the exception as described above.
I cannot go back to the earlier version of Ninject as that will mean not being able to add the new functionality that I'm adding.
From the research I've done so far the sharpfellows link above is the closest explanation of my issue that I've managed to find so far on the internet.
I don't use Ninject very often, so I've not got the background to know what's changed between V2 & V3 that (based on my research) is the cause of my issue.
I need to know how to change my code written under V2 (and shown above) so that it works under V3.
MissingMethodException is usually a deployment problem. You compile against a different assembly than you deploy. Check that you deployed the same version and same build.
So after a week or so it turns out that the problem was that the Nancy dev team broke binary comparability with the latest version of ninject (or vice versa) :-)
There is a GitHub pull request to fix this available at :
https://github.com/NancyFx/Nancy.Bootstrappers.Ninject/pull/6
However the next version 'Nancy.Bootstrapper.Ninject' 0.12 will be out on NuGet soon which will have the fix implemented.