Using IHtmlHelper with Dependency Injection - asp.net-core

I would create a controller that response with the third-party IHtmlContent created through IHtmlHelper interface without using View() method. Actually I'm using a workaround:
I have created an extension function of IHtmlHelper that behaves like a "master" and other static function that behaves like "slaves". The master function call slaves based on data structure that It has received as a parameter. Slaves create the IHtmlContent based on third-party library. When all slave functions are completed, the controller send back the response.
This behaviour is inside cshtml view called by controller.
Is possible to have IHtmlHelper as controller constructor parameter using dependency Injection?
Something like
public class MyTestController
{
private readonly IHtmlHelper _html;
public MyTestController(IHtmlHelper html) {
_html = html;
}
}
If it were possible I would simplify many operations because I'm using this Master/Slave functions to replicate the behaviours of a class
Here you can find a pure example of how my code works now and how I would like to change it
https://gist.github.com/Blackleones/eb0d02b9dd99164271af88e22143d72b#file-example-cs
I need this component because the third-party library is an extension of IHtmlHelper

I thank Itminus for answering me.
However call IHtmlHelper on controller constructor it is not enough, you must contextualize IHtmlHelper before using it.
I want to share another solution that I found and solve my problem, maybe It can be useful for other users.
Please see https://gist.github.com/Blackleones/eb0d02b9dd99164271af88e22143d72b#file-example-cs to remember what my goal is.
To solve my problem I have created a class called HtmlHelperAdapter that contains some instances I need.
public class MyHtmlHelperAdapter
{
public IHtmlHelper _html;
public NubessHtmlHelperAdapter(IHtmlHelper html)
{
_html = html;
}
public IHtmlHelper Html => _html;
public ViewContext ViewContext => _html.ViewContext;
public HttpContext HttpContext => _html.ViewContext.HttpContext;
public ViewDataDictionary ViewData => _html.ViewData;
public IServiceProvider provider => _html.ViewContext.HttpContext.RequestServices;
}
after that I have created an extension method for IHtmlHelper
public static class MyBuilderExtension
{
public static MyBuilder Nubess(this IHtmlHelper html)
{
return new MyBuilder(new MyHtmlHelperAdapter(html));
}
}
where MyBuilder is
public class MyBuilder
{
private readonly MyHtmlHelperAdapter _htmlHelper;
public MyBuilder(MyHtmlHelperAdapter htmlHelper)
{
_htmlHelper = htmlHelper;
}
public FormBuilder<object> Form<T>(IDictionary<string, string> customizeArgs = null, FormBuilder<object> externalForm = null)
{
return new FormBuilder(_htmlHelper, typeof(T), customizeArgs, externalForm).Build();
}
}
now I can create my custom contents for views using class thanks to MyHtmlHelperAdapter
public class FormBuilder
{
private MyHtmlHelperAdapter _htmlHelper;
private readonly IStringLocalizer<SharedResources> _localizer;
private readonly LinkGenerator _linkGenerator;
private readonly IEngine _engine;
private readonly IEngineElement _element;
private readonly Type _typeDescriptor;
private readonly IDictionary<string, string> _descriptorArgs;
/* variabili per semplificare la gestione del builder */
private readonly FormBuilder<object> Form;
private readonly FormConfig Config;
private readonly IList<FormGroupConfig> Groups;
private readonly IList<FormItemConfig> Items;
private readonly IDictionary<string, string> FormOptions;
private readonly string _clientPrefix = "smtForm_{0}_";
public FormBuilder(MyHtmlHelperAdapter htmlHelper, Type typeDescriptor, IDictionary<string, string> ModelCustomizeArgs = null, FormBuilder<object> externalForm = null)
{
_htmlHelper = htmlHelper;
_localizer = _htmlHelper.provider.GetRequiredService<IStringLocalizer<SharedResources>>() ?? throw new ArgumentNullException();
_linkGenerator = _htmlHelper.provider.GetRequiredService<LinkGenerator>() ?? throw new ArgumentNullException();
_engine = _htmlHelper.provider.GetRequiredService<IEngine>() ?? throw new ArgumentNullException();
//code..
}
public FormBuilder<object> Build()
{
//code..
return Form;
}
//methods..
}

Short answer : Yes .
If you look into the source code of MVC, you will find that the IHtmlHelper is registered as a transient service :
internal static void AddViewServices(IServiceCollection services)
{
...
//
// HTML Helper
//
services.TryAddTransient<IHtmlHelper, HtmlHelper>();
services.TryAddTransient(typeof(IHtmlHelper<>), typeof(HtmlHelper<>));
services.TryAddSingleton<IHtmlGenerator, DefaultHtmlGenerator>();
services.TryAddSingleton<ModelExpressionProvider>();
// ModelExpressionProvider caches results. Ensure that it's re-used when the requested type is IModelExpressionProvider.
services.TryAddSingleton<IModelExpressionProvider>(s => s.GetRequiredService<ModelExpressionProvider>());
services.TryAddSingleton<ValidationHtmlAttributeProvider, DefaultValidationHtmlAttributeProvider>();
services.TryAddSingleton<IJsonHelper, DefaultJsonHelper>();
...
}
In other words, you could inject the IHtmlHelper service as you like.
Just as a reminder, we should always prefer to using IHtmlHelper within View Layer.

Related

Using GetGridHtml in a controller in Umbraco 10

I have made a simple Api controller on my umbraco website, that simply searches some nodes for some properties and returns the content from those properties in json.
One of the properties is an umbraco grid. Technically that is also json, and I could successfully get the content with some elaborate JObject querying. But from the view side there is access to a "GetGridHtml" helper function that does that for you.
So my question is, how do I achieve that in a controller? I got the node as IPublishedContent which incidentally has that helper function, but it wants an IHtmlHelper which isn't available in this context. So what do I do?
[HttpGet]
[Route("api/myapp/version/")]
public IActionResult GetLatestMyAppVersion()
{
var releaseNote = _umbracoHelper.ContentAtXPath("//releaseNoteList").First().Children().OrderByDescending(x => x.Value<DateTime>("releaseDate")).First();
var ver = releaseNote.Value<string>("myAppReleaseVersion");
var date = releaseNote.Value<DateTime>("releaseDate");
var desc = releaseNote.GetGridHtml(?????, "mainContent", "Clean"); // this is where IHtmlHelper is needed
return Ok(new { Application = "MyApp", Version = ver, ReleaseDate = date.ToString("yyyy-MM-dd"), Description = desc });
}
If you want to get the Ihtmlhelper inside the controller method, I suggest you could inject it by inside the controller construct method and then you could use it inside your codes.
More details, you could refer to below codes:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IHtmlGenerator _htmlGenerator;
private readonly IHtmlHelper _helper;
public HomeController(IHtmlHelper helper, IHtmlGenerator htmlGenerator, ILogger<HomeController> logger)
{
_logger = logger;
_helper = helper;
_htmlGenerator = htmlGenerator;
}

Hangfire per-job correlationId/state

I run Hangfire on ASP.NET Core.
For our other projects we have CorrelationIds that we pass when making API calls to be able to link the caller and callee.
We use the IHttpContextAccessor's TraceIdentifier for this in ASP.NET Core.
Unfortunately it looks like the trick used by ASP.NET Core to get a scoped CorrelationId in the Transient IHttpContextAccessor doesn't work for Hangfire job execution.
Using a Scoped state correlation object doesn't work because it must be Transient to be able to work with the rest of the system (logging etc.)
I used to be able to get away using the ServiceLocator anti-pattern and resolve a scoped state object in a transient service.
In the latest ASP.NET Core that is no longer supported and an exception is thrown making the system too slow because of the huge number of exceptions thrown.
Is there something that Hangfire provides already that would give me a unique ID per job execution?
Cheers.
Thanks to jbl's comment I looked at what I was doing again and managed to get it working through a kludge.
I've got the transient state holder
(basically it's the HttpContextAccessor class renamed):
public class StateHolder
{
private static AsyncLocal<ContextHolder> _contextCurrent = new AsyncLocal<ContextHolder>();
public string State {
get {
return _contextCurrent.Value?.Context;
}
set {
var holder = _contextCurrent.Value;
if (holder != null)
{
holder.Context = null;
}
if (value != null)
{
_contextCurrent.Value = new ContextHolder { Context = value };
}
}
}
private class ContextHolder
{
public string Context;
}
}
and then in Hangfire I hook it up to the activation with
public class LoggingActivator : JobActivator
{
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ContextAccessor _contextAccessor;
public LoggingActivator([NotNull] IServiceScopeFactory serviceScopeFactory, ContextAccessor contextAccessor)
{
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
_contextAccessor = contextAccessor;
}
public override JobActivatorScope BeginScope(JobActivatorContext context)
{
return new LoggingActivatorScope(_serviceScopeFactory.CreateScope(), _contextAccessor);
}
}
and
public class LoggingActivatorScope : JobActivatorScope
{
private readonly IServiceScope _serviceScope;
private readonly ContextAccessor _contextAccessor;
public LoggingActivatorScope(
[NotNull] IServiceScope serviceScope,
ContextAccessor contextAccessor)
{
_serviceScope = serviceScope ?? throw new ArgumentNullException(nameof(serviceScope));
_contextAccessor = contextAccessor;
}
public override object Resolve(Type type)
{
_contextAccessor.Context = Guid.NewGuid().ToString();
return ActivatorUtilities.GetServiceOrCreateInstance(_serviceScope.ServiceProvider, type);
}
public override void DisposeScope()
{
_serviceScope.Dispose();
}
}
That seems to work fine.

How to inject dependency into NServiceBus pipeline behavior?

I've been following the NServiceBus samples, specifically for how to use an entity framework (core) DbContext integrated with Sql Persistence so that I can save dbcontext state changes along with the outbox messages. This is the sample: https://docs.particular.net/samples/entity-framework-core/
I've modified the unit of work code a little to support creation of an aspnet core DI scoped DbContext. The relevant code follows:
public class UnitOfWork<TDbContext>
where TDbContext : DbContext
{
private Func<SynchronizedStorageSession, IServiceProvider, TDbContext> _contextFactory;
private TDbContext _context;
private IServiceProvider _serviceProvider;
public UnitOfWork(Func<SynchronizedStorageSession, IServiceProvider, TDbContext> contextFactory, IServiceProvider serviceProvider)
{
_contextFactory = contextFactory;
_serviceProvider = serviceProvider;
}
public TDbContext GetDataContext(SynchronizedStorageSession storageSession)
{
if (_context == null)
{
_context = _contextFactory(storageSession, _serviceProvider);
}
return _context;
}
}
public class UnitOfWorkSetupBehavior<TDbContext> : Behavior<IIncomingLogicalMessageContext>
where TDbContext : DbContext
{
private readonly Func<SynchronizedStorageSession, IServiceProvider, TDbContext> _contextFactory;
private readonly IServiceScopeFactory _serviceScopeFactory;
public UnitOfWorkSetupBehavior(Func<SynchronizedStorageSession, IServiceProvider, TDbContext> contextFactory, IServiceScopeFactory serviceScopeFactory)
{
_contextFactory = contextFactory;
_serviceScopeFactory = serviceScopeFactory;
}
public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var uow = new UnitOfWork<TDbContext>(_contextFactory, scope.ServiceProvider);
context.Extensions.Set(uow);
await next().ConfigureAwait(false);
context.Extensions.Remove<UnitOfWork<TDbContext>>();
}
}
}
public static class EndpointConfigurationExtensions
{
public static void RegisterUnitOfWork<TDbContext>(this EndpointConfiguration endpointConfiguration, IServiceScopeFactory serviceScopeFactory)
where TDbContext : DbContext
{
var pipeline = endpointConfiguration.Pipeline;
pipeline.Register(new UnitOfWorkSetupBehavior<TDbContext>((storageSession, serviceProvider) =>
{
var dbConnection = storageSession.SqlPersistenceSession().Connection;
var dbContextFactory = serviceProvider.GetService<IDbContextConnectionFactory<TDbContext>>();
var dbContext = dbContextFactory.GetDbContext(dbConnection);
//Use the same underlying ADO.NET transaction
dbContext.Database.UseTransaction(storageSession.SqlPersistenceSession().Transaction);
//Call SaveChanges before completing storage session
storageSession.SqlPersistenceSession().OnSaveChanges(x => dbContext.SaveChangesAsync());
return dbContext;
}, serviceScopeFactory), "Sets up unit of work for the message");
}
}
public static class UnitOfWorkContextExtensions
{
public static TDbContext DataContext<TDbContext>(this IMessageHandlerContext context)
where TDbContext : DbContext
{
var uow = context.Extensions.Get<UnitOfWork<TDbContext>>();
return uow.GetDataContext(context.SynchronizedStorageSession);
}
}
For this to work the behavior needs an injected IServiceScopeFactory.
Now all examples I've been able to find of behavior registration only show the type manually instantiated and passed in to the endpointconfiguration's pipeline.
Is there a way to either gain access to an IServiceScopeFactory via the behavior's Invoke method (maybe by the context via some extension perhaps?), or is it possible to register the behavior itself such that I can construct it with services created by the DI container?
FYI I took a look at this Q&A which gave me the idea of injecting the IServiceScopeFactory. Unfortunately, the answer doesn't show how to actually get an instance of the interface.
You would use context.builder.Build<T>(); within the Invoke method to resolve any objects like IServiceScopeFactory.
https://docs.particular.net/samples/multi-tenant/di/
Make sure that the IServiceScopeFactory is registered in the DI container. For example, during your endpoint initialization:
endpointConfiguration.RegisterComponents(registration: x =>
{
x.ConfigureComponent<IServiceScopeFactory>(yourServiceScopeFactory);
});
https://docs.particular.net/nservicebus/dependency-injection/
You can also do this by creating a Feature
https://docs.particular.net/nservicebus/pipeline/features

How can I make an ASP Core Claims Principal available in a non-controller object?

I am working on an ASP Core 2 project using JWT authentication and the Dapper ORM.
Like all ASP projects, I have a lot of controllers, each instantiating its associated data objects. Each data object inherits from an abstract DbObject class that provides database access services. I also have an AuthenticatedUser object that abstracts the JWT to make it's properties easier to use.
What I want is to do is create the AuthenticatedUser object in the constructor of DbObject. Of course, one method is to create it in the controller and pass it to every concrete data object but this is messy as it would have to be passed hundreds of times (and it just feels wrong).
Is there a way to use the ASP Core middleware to get the token after authentication and make it available through dependency injection in the DbObject?
Edit
Hopefully, this clarifies my intentions. I would like the controller to create data objects and use their properties and methods without regard to implementation (i.e. DbObject). But queries executed by DbObject will be filtered by information in the token of the logged in user.
public class ManufacturerController : Controller {
[HttpGet]
public async Task<IActionResult> Get() {
var manufacturers = await new Manufacturer().SelectMany();
return Ok(manufacturers);
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id) {
var manufacturer = await new Manufacturer().SelectOne(id);
return Ok(manufacturer);
}...
public class Manufacturer : DbObject<Manufacturer> {
protected override string QrySelectOne => #"
Select *
From org.fn_Manufacturers ({0})
Where Id = {1}";
protected override string QrySelectMany => #"
Select *
From org.fn_Manufacturers ({0})";
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }...
public abstract class DbObject<T> {
protected readonly AuthenticatedUser authenticatedUser;
public DbObject(IHttpContextAccessor contextAccessor) {
authenticatedUser = new
AuthenticatedUser(contextAccessor.HttpContext.User);
}
protected abstract string QrySelectOne { get; }
protected abstract string QrySelectMany { get; }
public async Task<T> SelectOne (int id) {...}
public async Task<T> SelectOne(params object[] ids) {...}
public async Task<IEnumerable<T>> SelectMany () {...}
public async Task<IEnumerable<T>> SelectMany (params object[] ids) {...}
I suppose one solution may be to create a static data object factory which has the IHttpContextAccessor injected??
ASP.NET Core provides IHttpContextAccessor interface for accessing HttpContext from non-controller objects.
The usage is fair simple. Inject IHttpContextAccessor into DbObject and access HttpContext by calling IHttpContextAccessor.HttpContext:
public abstract class DbObject
{
protected DbObject(IHttpContextAccessor contextAccessor)
{
var context = contextAccessor.HttpContext;
// Create instance of AuthenticatedUser based on context.User or other request data
}
}
EDIT
Your controllers instantiate data objects directly (with new operator), that's why you can't have IHttpContextAccessor injected out of the box. Here are possible solutions. I list them in order of my preference (from best to worst).
If each controller uses only one (or just several) types of data objects, the best options will be to avoid direct instantiation and move toward normal Dependency Injection.
So if ManufacturerController requires only Manufacturer like in your sample then, it's better to inject Manufacturer instance to controller, not to create it inside:
public class Manufacturer1Controller : Controller
{
private readonly Manufacturer manufacturer;
public Manufacturer1Controller(Manufacturer manufacturer)
{
this.manufacturer = manufacturer ?? throw new ArgumentNullException(nameof(manufacturer));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturers = await manufacturer.SelectMany();
return Ok(manufacturers);
}
// ...
}
IHttpContextAccessor will be injected into Manufacturer and passed to base DbObject:
public class Manufacturer : DbObject<Manufacturer>
{
public Manufacturer(IHttpContextAccessor contextAccessor) : base(contextAccessor)
{
}
}
It's the cleanest solution in the list. You use DI in classic way and utilize all benefits DI provides.
If one controller could use dozens of different data objects, you could inject the factory object that creates instances of data objects. It could be simple implementation based on IServiceProvider:
public interface IDbObjectFactory
{
TDbObject Create<TDbObject>() where TDbObject : DbObject<TDbObject>;
}
public class DbObjectFactory : IDbObjectFactory
{
private readonly IServiceProvider serviceProvider;
public DbObjectFactory(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
public TDbObject Create<TDbObject>() where TDbObject : DbObject<TDbObject>
{
return serviceProvider.GetRequiredService<TDbObject>();
}
}
public class Manufacturer2Controller : Controller
{
private readonly IDbObjectFactory dbObjectFactory;
public Manufacturer2Controller(IDbObjectFactory dbObjectFactory)
{
this.dbObjectFactory = dbObjectFactory ?? throw new ArgumentNullException(nameof(dbObjectFactory));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturer = dbObjectFactory.Create<Manufacturer>();
var manufacturers = await manufacturer.SelectMany();
return Ok(manufacturers);
}
}
The code for Manufacturer and DbObject does not change comparing to the first option.
I don't see any reason not to use option #1 or #2. However just to complete the picture, I'll describe another two options.
Inject IHttpContextAccessor into conroller and pass this instance (or IHttpContextAccessor.HttpContext.User) to Data Object constructor invoked with operator new:
public class Manufacturer3Controller : Controller
{
private readonly IHttpContextAccessor contextAccessor;
public Manufacturer3Controller(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturer = await new Manufacturer(contextAccessor).SelectMany();
// or
// var manufacturer = await new Manufacturer(contextAccessor.HttpContext.User).SelectMany();
return Ok(manufacturer);
}
}
It's a bad solution, because you don't use Dependency Injection for Manufacturer here and loose many advantages that DI provides.
And the worst option would be using of static object factory with injected IHttpContextAccessor. With this approach you also loose benefits of DI. In addition you get ugly code somewhere in Startup that initializes static instance of IHttpContextAccessor. When you come to this approach, you'll discover that theere is no quite elegant way to do this.
My advice: use option #1 untill you have good reasons against it. Then use option #2.
Here is Sample Project on GitHub with samples for approaches ##1-3.

Get Injected Object in ASP.NET vNext filter

I'm trying to create my custom authorize attribute, but in asp.net vnext using the default dependency injection framework I don't how to get the injected object. I need to get the injected object in the default ctor.
public class CustomAttribute
{
private IDb _db;
public CustomAttribute()
{
_db = null; // get injected object
}
public CustomAttribute(IDb db)
{
_db = db;
}
// apply all authentication logic
}
You can use the ServiceFilterAttribute for this purpose. The service filter attribute lets the DI system take care of instantiating and maintaining the lifetime of the filter CustomAuthorizeFilter and its any required services.
Example:
// register with DI
services.AddScoped<ApplicationDbContext>();
services.AddTransient<CustomAuthorizeFilter>();
//------------------
public class CustomAuthorizeFilter : IAsyncAuthorizationFilter
{
private readonly ApplicationDbContext _db;
public CustomAuthorizeFilter(ApplicationDbContext db)
{
_db = db;
}
public Task OnAuthorizationAsync(AuthorizationContext context)
{
//do something here
}
}
//------------------
[ServiceFilter(typeof(CustomAuthorizeFilter))]
public class AdminController : Controller
{
// do something here
}