ASP.Net Core - How use custom 'ModelBindingMessageProvider' - asp.net-core

I have a simple ASP.Net Core app which uses razor. The user forms are generated by model and data annotation attributes. The error message of wrong input values are English by default. I knew that I can translate default message with help of 'ModelBindingMessageProvider'.
Below you can find a working way (in Startup.cs) by update the 'DefaultModelBindingMessageProvider':
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(o =>
{
// see: https://stackoverflow.com/questions/40828570/asp-net-core-model-binding-error-messages-localization
o.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(u => "My custom validation error message");
});
}
I would prefer not to configure the translation directly in the startup.cs. One way to do that is using a custom class which inherits from 'ModelBindingMessageProvider'.
public class MyModelBindingMessageProvider : ModelBindingMessageProvider
{
public override Func<string, string> ValueMustNotBeNullAccessor => o => "My custom validation error message";
}
Here is my question:
Where register My own class?
Is this a good way? What is best practice at the moment?
(the real app uses string resources of course)

Unfortunately it looks like it's not possible to provide a custom implementation of ModelBindingMessageProvider in ASP.NET Core 3.1.
The constructor of MvcOptions sets the ModelBindingMessageProvider property to an instance of DefaultModelBindingMessageProvider.
ModelBindingMessageProvider = new DefaultModelBindingMessageProvider();
And the property itself has no setter and is not of type ModelBindingMessageProvider.
public DefaultModelBindingMessageProvider ModelBindingMessageProvider { get; }
tl;dr Even though the option of having an own implementation would make sense here, you have to configure the messages in the Startup class.

Related

.Net Core : Class Library to connect to DB, DAL, User Secret and Asp.Net Core's Configuration

I have the following :
a class library with connection classes such as connection, command, parameter
a DAL with entities, mapper, interface, services as well as a static class that holds hard coded connectionString and InvariantName.
an Asp.Net Core project
References :
DAL has a reference to the class library to make use of its connection class to which it provides connectionString and InvariantName thanks to its static class etc..
Asp.Net has a reference to the DAL.
What I want :
I now want to use the User Secrets to store hard coded sensitive data connections and get rid off the static class.
I know I can use the the Asp.Net Core startup.cs to read the settings from Configuration and make use of binding to store them into a class and use DI.
My guess :
DI seems "easy" when used inside an Asp controller. But I need the settings values (connectionString and InvariantName) outside the Asp.Net Core to be injected into a constructor of a class somewhere in my DAL.
I guess I would then need to have to reference the Asp.Net Core project to my DAL. But then I would end up with a circular reference (DAL to Asp.Net Core and the opposite).
So what's the solution?
Have an intermediate library class into which I would retreive the settings values from Asp.Net Core and then pass them to my DAL (to prevent circular reference)?
Manually recreate the "Configuration process" inside the DAL and get settings there directly
Or something else that I don't know?
Ps : I am new in development and only have a few projects'experience in Asp.Net Framework so far..and it's my first Asp.Net Core project
I know I can use the the Asp.Net Core startup.cs to read the settings from Configuration and make use of binding to store them into a class and use DI
You already answered your own question with this. This is the correct and recommended behavior to setup DI for 3rd party libs and configurations. If you want to avoid clutter in Startup class, create an extension method:
namespace Microsoft.Extensions.DependencyInjetion
{
public static MyLibraryCollectionExtensions
{
public static IServiceCollection AddMyLibrary(this IServiceCollection services)
{
services.AddDbContext<MyDbContext>(...);
}
}
}
to register your classes. Alternatively, extend the method to accept a parameter delegate to configure it
namespace Microsoft.Extensions.DependencyInjetion
{
public static MyLibraryCollectionExtensions
{
public static IServiceCollection AddMyLibrary(this IServiceCollection services, Action<MyOptions> setup)
{
var defaultOptions = ... // i.e. new MyOptions();
// pass default options to be modified by the delegate
setup?.Invoke(defaultOptions);
// your registrations
services.AddDbContext<MyDbContext>(...);
}
}
}
And all the user has to do in your library is add
services.AddMyLibrary();
// or with setup
services.AddMyLibrary(config =>
{
config.MyConnectionString = Configuration.GetConnectionString("MyContext");
});
and store the connection string in the appsettings.json.
{
"ConnectionStrings":
{
"MyContext" : "MyConnectionString here"
}
}
I finally used the ConfigurationBuilder to get values from the appsettings.json file.
It's probably not the right way to do it but it is working with my DAL and Connection dlls.
In case it helps anyone else :

.NET Core DI passing info of AddHttpContextAccessor to a service

In my solution I have projects for my API, my Web App and also have another project which includes services, that are getting some information from a database and formatting them, these are currently only used by this API, but these could be used by other API projects in the future.
My API have a couple controllers that are returning JSON data from the result returned by the services.
In some cases the services needs to call the API to process some information before calling the request to the database. Since I have dev/staging/prod environment with their own URL I don't want to hardcode the URLs in the services I want to use DI to get these dynamicaly depending on the context.
In the Startup.cs of my API I have added services.AddHttpContextAccessor(); in the ConfigureServices(IServiceCollection services) section to gain access to the current http context :
public void ConfigureServices(IServiceCollection services)
{
...
services.AddHttpContextAccessor();
...
}
With that I know I can now access the information directly into my controller which I tried and it worked :
public class DataController : ControllerBase
{
...
private readonly string _baseUrl;
public FeaturesController(...
,IHttpContextAccessor httpContextAccessor)
{
...
_baseUrl = UrlHelpers.ShowBaseURL(httpContextAccessor) ?? throw new ArgumentNullException(nameof(_baseUrl));
}
}
public static class UrlHelpers
{
public static string ShowBaseURL(IHttpContextAccessor httpcontextaccessor)
{
var request = httpcontextaccessor.HttpContext.Request;
var absoluteUri = string.Concat(
request.Scheme,
"://",
request.Host.ToUriComponent(),
request.PathBase.ToUriComponent());
return absoluteUri;
}
}
I could do just about the same thing in the services but to me they should not act directly on the httpcontext, since this is not the job they are meant to do. I am sure I could do better by adding a class injected of some sort that would have then make the specific value available to my services.
I know I could also pass the _baseUrl directly as an argument when calling the services from my controller but since I am trying to better understand DI and use it I would rather find another way if it is viable.
I can't give credit but I went with Steven solution which make the most sens

The DbContext of type cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions

I am trying to upgrade our current .Net Core application from 1.1 to 2.0 and am getting this runtime error: "The DbContext of type 'CoreContext' cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions".
It is caused by using the new IServiceCollection.AddDbContextPool<> function. When I use IServiceCollection.AddDbContext<> it still works.
This application is DB-First, so I generate all our contexts using 'Scaffold-DbContext'. Due to that, and the need to inject other services I have an extension on every context like this:
public partial class CoreContext
{
public CoreContext(
DbContextOptions<CoreContext> options,
IUserService userService,
IAuditRepository auditRepository
) : base(options) {...}
}
Whenever I run the Scaffold-DbContext I just remove the autogenerated Constructor from CoreContext, but even if I put it in there I still get this error.
public partial class CoreContext : DbContext
{
public CoreContext(DbContextOptions<CoreContext> options) : base(options) {}
}
I've already updated Program.cs to the new style:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
}
And the Startup.cs is pretty straightforward:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
...
services.AddDbContextPool<CoreContext>(options => options.UseSqlServer(absConnectionString));
...
}
I am using Autofac for DI if that helps. For now I'll default back to the non-Pooling alternative, but it would be nice to take advantage of this feature.
When using DbContext Pooling, your own state (e.g. private fields) in your derived DbContext class will be preserved. Which means the lifetime of your services is now singleton. That's why you shouldn't have other injected services here.
But it's possible to query the required services this way:
First we should use the UseInternalServiceProvider method on DbContextOptionsBuilder to tell EF which service provider to use for its services. This service provider must have all the services configured for EF and any providers. So we should register EF Services manually:
services.AddEntityFrameworkSqlServer();
And then introduce the application's services provider which now includes the EF Services too:
services.AddDbContextPool<ApplicationDbContext>((serviceProvider, optionsBuilder) =>
{
optionsBuilder.UseSqlServer("...");
optionsBuilder.UseInternalServiceProvider(serviceProvider);
});
After that define these namespaces:
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
And now you can access the registered services in the application within the
ApplicationDbContext class using the following methods
var siteSettings = this.GetService<IOptionsSnapshot<SiteSettings>>();
Or
var siteSettings = this.GetInfrastructure().GetRequiredService<IOptionsSnapshot<SiteSettings>>();
this is the current instance of the DbContext.
Remove the default constructor in the DbContext class, this worked for me
"because it does not have a single public constructor accepting a
single parameter of type DbContextOptions"
If you have any public constructors apart from one that accepts DbContextOptions, you need to remove them or make them non-public in order to use context pooling.
Also, there are restrictions on what can be done by overriding the OnConfiguring method. This is referenced in the documentation here but it isn't explicit about what those restrictions are: https://learn.microsoft.com/en-us/ef/core/what-is-new/index#dbcontext-pooling
This issue is mostly encountered when you "Scaffold-Dbcontext" and two constructors are generated.
Simple Solutions:
AddDbContextPool:
If you want to use AddDbContextPool, remove your empty constructor and maintain the one with the DbContextOptionsBuilder. Note that in this case you might have to provide the options, as suggested in the previous posts.
AddDbContext:
With AddDbContext, you can have both constructors/Overloads
Note: AddDbContextPool is preferred for performance reasons!
Try to use AddDbContext instead of AddDbContextPool. This helped me in the same situation.
services.AddDbContext<CoreContext>(options => options.UseSqlServer(absConnectionString));
in some case need to
remove the constractor with zero parameter
//public MyContext()
//{
//}
or use
"AddDbContext"
instead of
"AddDbContextPool"
in startup.cs => ConfigureServices()
services.AddDbContext(options =>
options.UseSqlServer(absConnectionString));

How to resolve dependency inside AuthorizeAttribute with WebApi and Ninject

My current setup is using Ninject for simple IoC, everything goes fine, but I'm not able to resolve one of the classes I need inside my AuthorizeAttribute. I need to access a class that does ClaimsVerification:
Here's my code:
IoC Config:
var kernel = new StandardKernel(); // Ninject IoC
// These registrations are "per instance request".
// See http://blog.bobcravens.com/2010/03/ninject-life-cycle-management-or-scoping/
kernel.Bind<RepositoryFactories>().To<RepositoryFactories>()
.InSingletonScope();
kernel.Bind<IRepositoryProvider>().To<RepositoryProvider>();
kernel.Bind<ISmartDocumentorUow>().To<SmartDocumentorUow>();
kernel.Bind<IClaimsVerification>().To<ClaimsVerification>();
// kernel
//kernel.BindFilter<MyAuthorizeAttribute>(FilterScope.Controller, 0).WhenControllerHas<RequireRolesAttribute>();
// Tell WebApi how to use our Ninject IoC
config.DependencyResolver = new NinjectDependencyResolver(kernel);
MyAuthorizeAttribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
[Inject]
IClaimsVerification clamisverify { get; set; }
public MyAuthorizeAttribute()
{
//var x = System.Web.Mvc.DependencyResolver.Current.(typeof(IClaimsVerification));
}
Yap, sorry, the problem was injecting the iClaimsverification that isn't working in web api..
I tryed with the public property and still it didn't work.
the bindfilter is commented out, because it doesn't exist in the core NInject api (dll), it does exists in the MVC dll of ninject but it works for Action filters in the web mvc, and not in the api mvc for what i can tell..
i do solved the issue like this, though i don't like a lot of this fix:
private IClaimsVerification verifier
{
get
{
return (GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IClaimsVerification)) as IClaimsVerification);
}
}
The property you have marked with Inject is private - you need to initialize Ninject with a custom configuration to opt into what would be a much less efficient process
(You didnt state the problem in your question. I see you were trying BindFilter, but it's commented out (why?) - this is the correct approach. I recommend reading the Ninject.MVC3 wiki article on BindFilter for an example)

Ninject, Linq to Sql, request scope for each controller without injecting

I recently came across this article titled:
Linq to Sql and ASP.NET MVC – DataContext Per Request
at this link:
http://www.jeremyskinner.co.uk/2010/01/31/linq-to-sql-and-asp-net-mvc-datacontext-per-request/
I would like to set this up using ninject rather than structuremap preferably using the new mvc 3 dependency resolver as I'm using mvc 3 rtm.
The relevant part of the article is this:
Firstly, you’ll need to configure StructureMap by calling ObjectFactory.Configure inside your Global.asax passing in a custom Registry instance:
protected void Application_Start() {
RegisterRoutes(RouteTable.Routes);
ObjectFactory.Configure(cfg => {
cfg.AddRegistry(new MyRegistry());
});
}
The code for MyRegistry looks like this:
public class MyRegistry : Registry {
public MyRegistry() {
For<BlogDataContext>()
.HttpContextScoped()
.Use(c => new BlogDataContext());
Scan(scan => {
scan.AddAllTypesOf<Controller>();
});
}
}
Here I’m telling StructureMap to create one instance of my BlogDataContext per HTTP Request as well as registering each Controller instance with the container.
Next, we need to tell MVC to use StructureMap to instantiate our controllers. This can be done by creating a custom ControllerFactory:
public class StructureMapControllerFactory : DefaultControllerFactory {
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
return (IController) ObjectFactory.GetInstance(controllerType);
}
}
We can then replace the DefaultControllerFactory with the StructureMapControllerFactory in our Application_Start:
protected void Application_Start() {
RegisterRoutes(RouteTable.Routes);
ObjectFactory.Configure(cfg => {
cfg.AddRegistry(new MyRegistry());
});
ControllerBuilder.Current.SetControllerFactory(
new StructureMapControllerFactory());
}
I would like to do the same thing with ninject 2.0 rather than structure map. I'm building an mvc 3 site with ninject mvc3. I downloaded the ninject mvc 3 package from nuget and I have this file in my solution which handles wiring up ninject.
AppStart_NinjectMVC3.cs
I do not want to use structurmap and I know the same setup can be done with ninject, but I'm unsure how to wire it up.
Thank you.
I'd rather use the official mvc3 extension from the ninject project found at https://github.com/ninject/ninject.web.mvc. It comes with a full example application showing how to wire up an mvc3 application.