Global Variables in ASP.Net Core 2.2 - c#

I'm trying to get more clarification on an already asked question. Because I'm a new user, I can't comment yet (You must have 50 reputation to comment).
In regards to Global Variables in ASP.Net Core 2
#nurdyguy comments that "The dependency injection built in to the framework will populate the options variable".
var repo = new DB();
More code to demonstrate the problem:
public class Secrets
{
public string ConnectionString { get; set; }
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
.
.
.
System.Action<Secrets> opts = (opt =>
{
opt.ConnectionString = Configuration["ConnectionString"];
});
services.Configure(opts);
services.AddSingleton(resolver => resolver.GetRequiredService<IOptions<Secrets>>().Value);
}
}
public sealed class DB
{
private string _connectionString;
public DB(Secrets secrets)
{
_connectionString = secrets.ConnectionString;
}
}
public class testModel : PageModel
{
public void OnGet()
{
DB db = new DB();
}
}
When I attempt to instantiate the class, I receive a the following compile time error:
Error CS7036
There is no argument given that corresponds to the required formal parameter 'secrets' of 'DB(Secrets)'
I've followed the code example correctly I'm sure. Is their another way to instantiate the class?

The main problem is you are trying to create an instance of PropertySalesRepository using new that calls the constructor that requires a prop of the type MDUOptions.
When creating an instance with new you do not get the class with injected properties.
To get the instance of the class with injected properties the easiest way is to inject it into your controller.

Related

I am trying to inject a service into a constructor using ASP.Net Dependency Injection but getting a message that my blank constructor is not enough

Here is what I have so far. I am trying to create a new ThemeManagementViewModel and inject into that a resource service using:
Microsoft.Extensions.DependencyInjection version 5.0.1 nuget package
public static class Startup
{
public static IServiceProvider ServiceProvider { get; set; }
public static IServiceProvider Init()
{
var serviceProvider = new ServiceCollection().ConfigureServices()
.BuildServiceProvider();
ServiceProvider = serviceProvider;
return serviceProvider;
}
}
public static class DependencyInjectionContainer
{
public static IServiceCollection ConfigureServices(this IServiceCollection services)
{
services.AddSingleton<IDatabaseService, DatabaseService>();
services.AddSingleton<IResourceService, ResourceService>();
services.AddTransient<ThemeManagementViewModel>();
return services;
}
}
public partial class ThemeManagementViewModel : BaseViewModel
{
private readonly IResourceService _resourceService;
public ThemeManagementViewModel(IResourceService resourceService)
{
_resourceService = resourceService;
}
}
public partial class ResourceService : IResourceService
{
private IDatabaseService _databaseService;
public ResourceService(IDatabaseService databaseService)
{
_databaseService = databaseService;
}
}
public interface IResourceService
{
void SetResourceColors();
}
public class ThemeManagementPage : HeadingView
{
private readonly ThemeManagementViewModel _vm;
public ThemeManagementPage()
{
BindingContext = _vm = new ThemeManagementViewModel();
}
}
When I build my application it gives me a message for this line:
BindingContext = _vm = new ThemeManagementViewModel();
and this is the message that I am getting.
There is no argument given that corresponds to the required
formal parameter 'resourceService' of
'ThemeManagementViewModel.ThemeManagementViewModel(IResourceService)'
I thought that the DI was supposed to insert the service into the constructor of ThemeManagementViewModel but it seems not to be working.
Dependency injection will not simply take place anywhere where you construct an object. You need to go explicitly through your DI framework.
In this case, you need to call GetRequiredService() of your IServiceProvider object.
var _vm = Startup.ServiceProvider.GetRequiredService<ThemeManagementViewModel>();
Also, from your code, we don't see that you use your DependencyInjectionContainer class at all. You must make sure that your ConfigureServices method is called explicitly.
DI cannot do magic. The compiler doesn't know anything about it. You have to use it explicitly. It looks like it could do magic in the context of ASP.net website projects. But that is only because there it is the ASP.net framework that handles the things that you need to do explicitly in other types of projects.
Tutorial on how to use DI in .net applications

Dependency injection between two ASP.NET Core projects

I'm currently developing a web application with ASP.NET Core and handling the database with Entity Framework Core. I have two projects in my VS Solution; WebApp (the main application) and DatabaseHandler (the EF Core handler). I have installed Entity Framework Core with the Pomelo package, since I'm using a MySQL database.
I've been following the Microsoft documentation to setup EF Core, connection strings and all that, and it works fine. I'm able to make migrations, make updates and do stuff with the database. I'm however not sure if I'm doing it correctly, since the latest EF Core tutorials use dependency injection and I'm not familiar with it.
Right now I'm passing the DbContext object as an argument from WebApp to DatabaseHandler, since I want all database-related stuff to only exist in DatabaseHandler. This works, but is it possible to call functions from another project and also share the DbContext object without passing it as an argument? I'm probably not explaining it well, I hope my code explains it better.
WebApp/Startup.cs:
This is where I load the connection string from appsettings.json.
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextPool<DataContext>(
options => options.UseMySql(Configuration.GetConnectionString("DefaultConnection")
));
services.AddRouting(options => options.LowercaseUrls = true);
services.AddControllersWithViews();
}
WebApp/HomeController.cs:
This is where I call the GetAllChallenges() function from the DatabaseHandler project, and I also pass the DataContext object as an argument. This is what I'm trying to avoid!
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly DataContext db;
public HomeController(ILogger<HomeController> logger, DataContext _db)
{
_logger = logger;
db = _db;
}
public IActionResult Challenges()
{
List<Challenge> ChallengesList = DatabaseHandler.HandleChallenges.GetAllChallenges(db);
return View(ChallengesList);
}
}
DatabaseHandler/DataContext.cs:
This is where I initialize the entity classes and so on.
public class DataContext : DbContext
{
public DataContext(DbContextOptions<DataContext> options) : base(options) { }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { }
// Tables
public DbSet<User> Users { get; set; }
public DbSet<Challenge> Challenges { get; set; }
// Data seeding
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Seed();
}
}
DatabaseHandler/HandleChallenges.cs:
This is where I have all my database functions. The results are returned back to the controller within the WebApp project.
public class HandleChallenges
{
public static List<Challenge> GetAllChallenges(DataContext db)
{
var Data = db.Challenges;
List<Challenge> ChallengesList = Data.ToList();
return ChallengesList;
}
}
I have looked into dependency injection, but I'm not sure how I can use this between two projects. Is there a less complicated way of achieving this, perhaps without using DI at all? I'm satisfied as long as I don't need to pass the DataContext object as an argument every time I need to call a function from DatabaseHandler.
Can someone help me understand? Thanks a lot in advance!
You could use Options pattern, which I have already used many times. Its working very well despite of database you use. Thanks to dependency injection you are able to access if from multiple projects. Reading documentation about Option pattern (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-3.1) is useful but I will also provide you with my own example :
First you create model to store you connection string, dbName etc. Remember to add it in a library outside your main project(eg. Web Api) :
public class NameOfYourProject_ApiDbSettings : IIMTTApiDbSettings
{
public NameOfYourProject_ApiDbSettings()
{
}
public string CollectionName { get; set; }
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}
public interface I_NameOfYourProject_ApiDbSettings
{
string CollectionName { get; set; }
string ConnectionString { get; set; }
string DatabaseName { get; set; }
}
Secondly you make it available for all you projects :
services.Configure<NameOfYourProjectApiDbSettings>(options =>
{
options.ConnectionString
= Configuration.GetSection("NameOfYourProjectDbSettings:ConnectionString").Value;
options.DatabaseName
= Configuration.GetSection("NameOfYourProjectDbSettings:DatabaseName").Value;
});
Then you can use it in multiple projects. (Rememebr to add referance to you model -> point 1. I keep the model always with repository) I will give you my example where I use MongoDb :
private readonly IMongoDatabase _database = null;
public SomeObjectContext(IOptions<IMyProjectDbSettings> settings)
{
var client = new MongoClient(settings.Value.ConnectionString);
if (client != null)
_database = client.GetDatabase(settings.Value.DatabaseName);
}
public IMongoCollection<MyModel> MyModels
{
get
{
return _database.GetCollection<MyModel>("MyModels");
}
}
You need to extract an interface from the class (note the method is no longer static) and add a constructor for the context:
public interface IHandleChallenges
{
List<Challenge> GetAllChallenges();
}
public class HandleChallenges : IHandleChallenges
{
public HandleChallenges(DataContext context)
{
db = context;
}
private DataContext db;
public List<Challenge> GetAllChallenges()
{
var Data = db.Challenges;
List<Challenge> ChallengesList = Data.ToList();
return ChallengesList;
}
}
Then register it as a service:
services.AddScoped<IHandleChallenges, HandleChallenges>();
Your controller now receives this class in it's constructor instead of the context:
private IHandleChallenges _challengeHandler;
public HomeController(ILogger<HomeController> logger, IHandleChallenges challengeHandler)
{
_logger = logger;
_challengeHandler = challengeHandler;
}
And calls it from the action:
public IActionResult Challenges()
{
List<Challenge> ChallengesList = _challengeHandler.GetAllChallenges();
return View(ChallengesList);
}

Model validation for configuration objects in ASP.NET Core

In .NET Core, if my appsettings file looks like
{
"Section": {
"Field": "value"
}
}
I can create a class like
public class Section
{
public string Field { get; set; }
}
and retrieve the value in Startup like
public void ConfigureServices(IServiceCollection services) {
services.Configure<Section>(this.Configuration.GetSection("Section"));
}
The problem is that if for some reason (say misspelling) the binding fails, it is not going to throw, and instead it will create a Section object with null (default) value for the Field property.
Is there a way to make services.Configure<Section>(this.Configuration.GetSection("Section")); to throw if the binding fails?
I am just summing up #Nkosi's answer here which makes this validation possible using data annotation.
1- Annotate the properties of your class:
public class Section
{
[Required]
public string Field { get; set; }
}
2- Create an extension method to enable validation to take effect:
public static class ConfigurationModelValidation
{
public static T GetValid<T>(this IConfiguration configuration)
{
var obj = configuration.Get<T>();
Validator.ValidateObject(obj, new ValidationContext(obj), true);
return obj;
}
}
3- In the Startup class, register you configuration models as below using GetValid method (instead of using 'IOptions'):
public void ConfigureServices(IServiceCollection services) {
services.AddSingleton(this.Configuration.GetSection("Section").GetValid<Section>());
}
4- Now in the user's class directly inject your configuration model:
public class MyClass
{
private readonly string field;
public MyClass(Section section)
{
this.field = section.field;
}
}
Now if binding fails for any reason, the validation will kick in and it will throw, enjoy!
You can just get the section first, then verify it exists (because it will not be null).
public void ConfigureServices(IServiceCollection services) {
var section = this.Configuration.GetSection(nameof(Section));
if (!section.Exists()) throw new Exception();
services.Configure<Section>(section);
}

How to instantiate a DbContext in EF Core

I have setup .net core project and db context also. But i cant start using dbContext yet due this error-
"there is no argument given that corresponds to the required formal
parameter 'options'"
Controller:
public IActionResult Index()
{
using (var db = new BlexzWebDb())
{
}
return View();
}
Dbcontext Code:
public class BlexzWebDb : DbContext
{
public BlexzWebDb(DbContextOptions<BlexzWebDb> options)
: base(options)
{ }
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<AssignedRole> AssignedRoles { get; set; }
}
error picture attached. How can this issue be fixed?
Instantiate new object of DbContext from ConnectionString
var connectionstring = "Connection string";
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer(connectionstring);
ApplicationDbContext dbContext = new ApplicationDbContext(optionsBuilder.Options);
// Or you can also instantiate inside using
using(ApplicationDbContext dbContext = new ApplicationDbContext(optionsBuilder.Options))
{
//...do stuff
}
Note
At the time of writing the use of EF Core with the Dependency injection framework wasn't as known as it is now. This answers gives answer to the question from a DI perspective, which at the time, helped out OP.
The other answer provides you a conventional way to instantiate the DbContext using the new operator.
TL;DR, 3 options:
Option 1
Register the DbContext during application configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextPool<BlexzWebDb>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BlexzWebConnection")));
}
and use the DI framework to retrieve it:
public class SomeController : Controller
{
private readonly BlexzWebDb _db;
//the framework handles this
public SomeController(BlexzWebDb db)
{
_db = db;
}
}
Option 2
If you are looking for a design-time IdentityDbContext using IOptions<OperationalStoreOptions>, see: Add migration for ApiAuthorizationDbContext from another project - EF Core
Option 3
Or use the new operator and provide the details, see #Qamar Zaman's answer for details.
The long answer, and why DI is a treat
In EF Core it's common to pass some DbContextOptions to the constructor.
So in general, a constructor looks like this:
public BlexzWebDb(DbContextOptions<BlexzWebDb> options) : base(options)
As you can see there, there is no valid overload in the form of a parameter-less constructor:
Thus, this does not work:
using (var db = new BlexzWebDb())
Obviously, you can pass in an Option object in the constructor but there is an alternative. So,
Instead
.Net Core has IoC implemented in it's roots. Okay, this means; you don't create a context, you ask the framework to give you one, based on some rules you defined before.
Example: somewhere you will register your dbcontext, (Startup.cs):
//typical configuration part of .net core
public void ConfigureServices(IServiceCollection services)
{
//some mvc
services.AddMvc();
//hey, options!
services.AddDbContextPool<BlexzWebDb>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BlexzWebConnection")));
//...etc
Now the registering part is done, you can retrieve your context from the framework. E.g.: inversion of control through a constructor in your controller:
public class SomeController : Controller
{
private readonly BlexzWebDb _db;
//the framework handles this
public SomeController(BlexzWebDb db)
{
_db = db;
}
//etc.
why?
So, why not just provide the arguments and new it?
There is nothing wrong with the use of new - there are a lot of scenario's in which it works best.
But, Inversion Of Control is considered to be a good practice. When doing asp dotnet core you're likely to use it quite often because most libraries provide extension methods to use it. If you are not familiar with it, and your research allow it; you should definitely give it a try.
Therefore, instead of providing "just a way to instantiate" the object, I'll try to get you onto this track - inline with the framework. It will save you some hassle afterwards. Besides, otherwise "use an activator's CreateInstance" would just be as valid as an answer ;-)
Some links:
MSDN Fundamentals
MSDN Dependency Injection
Wikipedia Inversion Of Control
As addition of #Stefan's answer there is another way to achieve this. You can set db connection string in OnConfiguring method of DbContext class without adding DbContext service in startup.cs.
Setting.cs
public static class Setting
{
public static string ConnectionString { get; set; }
}
Startup.cs
Setting.ConnectionString = Configuration.GetSection("ConnectionStrings:BlexzDbConnection").Value;
BlexzWebDb.cs
public class BlexzWebDb : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(Setting.ConnectionString);
}
}
}
HomeController.cs
public class HomeController : Controller
{
private readonly BlexzWebDb db;
public HomeController()
{
this.db = new BlexzWebDb();
}
//etc.
Code sample for EF Core 3.1:
public class Test
{
private readonly IServiceProvider _serviceProvider;
public Test(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task<RequestResult> Handle(...)
{
await using var context = CreateContext();
...
}
private DocumentContext CreateContext()
{
var options = _serviceProvider.GetService<IOptions<DocumentContextOptions>>();
return new DocumentContext(options);
}
}

Unable to resolve service for type while attempting to activate

In my ASP.NET Core application, I get the following error:
InvalidOperationException: Unable to resolve service for type 'Cities.Models.IRepository' while attempting to activate 'Cities.Controllers.HomeController'.
I the HomeController I am trying to pass the Cities getter to the view like so:
public class HomeController : Controller
{
private readonly IRepository repository;
public HomeController(IRepository repo) => repository = repo;
public IActionResult Index() => View(repository.Cities);
}
I have one file Repository.cs that contains an interface and its implementation like so:
public interface IRepository
{
IEnumerable<City> Cities { get; }
void AddCity(City newCity);
}
public class MemoryRepository : IRepository
{
private readonly List<City> cities = new List<City>();
public IEnumerable<City> Cities => cities;
public void AddCity(City newCity) => cities.Add(newCity);
}
My Startup class contains the default-generated code from the template. I have made any changes:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
}
}
For the Dependency Injection framework to resolve IRepository, it must first be registered with the container. For example, in ConfigureServices, add the following:
services.AddScoped<IRepository, MemoryRepository>();
For .NET 6+, which uses the new hosting model by default, add the following in Program.cs instead:
builder.Services.AddScoped<IRepository, MemoryRepository>();
AddScoped is just one example of a service lifetime:
For web applications, a scoped lifetime indicates that services are created once per client request (connection).
See the docs for more information on Dependency Injection in ASP.NET Core.
We are getting this error in Entity frame work core database first approach. I followed below steps and error got resolved
Step 1: Check Your context class constructor should be like this
public partial class ZPHSContext : DbContext
{
public ZPHSContext(DbContextOptions<ZPHSContext> dbContextOptions)
: base(dbContextOptions)
{
}
}
Step 2: In Startup file
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ZPHSContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("BloggingDatabase")));
}
Step 3: Connection string in appsettings
"ConnectionStrings": {
"BloggingDatabase": "Server=****;Database=ZPHSS;Trusted_Connection=True;"
}
Step 4: Remove default code in OnConfiguring method in context class
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
}
Other answers are CORRECT, however I was spinning up a new asp.net core 2.1.x project and got this error.
Ended up being a typo by ME.
So in my Controller instead of Correctly using the Interface like this
public HomeController(IApplicationRepository applicationRepository)
{
_applicationRepository = applicationRepository;
}
My typo had me using ApplicationRepository instead of its interface IApplicationRepository
Notice below, and so with NO ERRORS spotting the missing "I" was fun :/
public HomeController(IApplicationRepository applicationRepository)
{
_applicationRepository = applicationRepository;
}
Thus the controller was not resolving the DI...
A method like this needs to be added to your Startup:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//...
// Add application services.
services.AddTransient<IRepository, MemoryRepository>();
//..
}
Services should be registered before used.
UPDATE:
If you do not want to use DI on your application, just create and instance of MemoryRepository on the constructor of HomeController, like this:
public class HomeController : Controller
{
private IRepository repository;
public HomeController()
{
repository = new MemoryRepository();
}
public IActionResult Index()
{
return View(repository.Cities);
}
}
You have to add your implementation to DI (Dependeny Injection) section. For .Net Core Mvc, it would be like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase()
);
services.AddScoped<IRepository, MemoRepostory>();
}
This may not be helpful for your code sample but in my case the same error was a result of a circular dependency.
you have to register your repository like this
services.AddSingleton<IRepository, MemoryRepository>();
In my case, I was trying to access context through constructor. like here;
private readonly Context _context;
public ImageController(Context context)
{
_context = context;
}
But When I tried to access the context just by creating an instance of class, it worked like here;
Context c = new Context();
For me I am using visual studio 2022 and .NET 6
the solution was add the following line in the Program.cs file :
builder.Services.AddSingleton<IHISInterface<UserDetails>, UserDetailsRepository>();
There is one more possibility that, You might have sent wrong variable in the place while writing this HTTPPOST last part code
mine is
var categoryMap = _mapper.Map(categoryCreate);
if(!_categoryRepository.CreateCategory(categoryMap))
{
ModelState.AddModelError("", "Something went wrong while saving");
return StatusCode(500, ModelState);
}
return Ok("Successfully created");
in the if condition I passed the category as parameter instead of categoryMap
so please cross check

Categories

Resources