Migration to .net6, problem with settings - asp.net-core-webapi

I am moving code from .net 4.6.1 to .net 6 API. There is a lot of code using: ConfigurationManager.AppSettings["something"].ToString()
to read App.config
In .net6 we use appsettings.json and read it with dependency injection
for example:
private readonly IOptions<Configs> _configs;
public WeatherForecastController(IOptions<Configs> configs)
{
_configs = configs;
}
public IEnumerable<WeatherForecast> Get()
{
var valFromConfig = _configs.Value.Val;
}
How can read settings without passing _configs as parameter of the method?
public IEnumerable<WeatherForecast> Get()
{
var ret = MyClass.GetConfigVal();
}
where MyClass in old solution:
public class MyClass
{
public static string GetConfigVal()
{
return ConfigurationManager.AppSettings["something"].ToString(); // How to change this
}
}

in .net 6 ,You could inject IConfigration into your controller:
private readonly IConfiguration _configuration;
public WeatherForecastController(IConfiguration configuration)
{
_configuration = configuration;
}
public IEnumerable<WeatherForecast> Get()
{
var valFromConfig = _configuration.GetSection(".....").Value;
}
For more details,you could check this document:

So I create classes for setting
public static class ApplicationSettings
{
public const string options = "Appsettings";
public static AppSettings AppSettings { get; private set; } = new AppSettings();
}
public class AppSettings
{
public string SomeSetting { get; set; } = string.Empty;
}
Appsetting.json look like
{
"AppSettings": {
"SomeSetting": "some text"
}
}
Add this to program.cs
ConfigurationManager configuration = builder.Configuration;
configuration.GetSection(ApplicationSettings.options).Bind(ApplicationSettings.AppSettings);
So I can use it everywhere
protected static readonly AppSettings AppSettings = ApplicationSettings.AppSettings;
var mySetting = AppSettings.SomeSetting

Related

There is no service for type has been registered Exception coming in Blazor server side application

This is Razor form code
#inherits OwningComponentBase<SignatureColoursServices>
List Displaydata=new List();
protected override void OnInitialized()
{
Displaydata = Service.GetSignatures();
}
This is the code in Startup.cs:
services.AddSingleton<ISignatureColoursServices,SignatureColoursServices>();
This is SignatureColour.cs
public string Value { get; set; } = null!;
public string Description { get; set; } = null!;
This is signaturecolourservices.cs:
protected readonly MyDbContext _dbcontext;
public SignatureColoursServices(MyDbContext db)
{
_dbcontext = db;
}
public List<SignatureColour> GetSignatures()
{
return _dbcontext.SignatureColours.ToList();
}
I have tried adding singleton and transient and scoped in startup.cs
You needed to register the service properly. Register SignatureColourServices in the Program.cs file. Please check the below example.
builder.Services.AddScoped<IEmployeeService, EmployeeService>();
builder.Services.AddScoped<ICustomerService, CustomerService>();
builder.Services.AddScoped<SignatureColourServices>();

How to inject my AddDbContext<ContainerContext> into my DAL project (Core 3.1)

I'm using MVC 5, Core 3.1
I have 'AddDbContext' added to my service in Startup.cs.
I then have a Class library core 3.1 project which is my ADO Dal layer.
This is added as a service as well in The ConfigureServices of Startup.cs.
I want to inject the Connection String into the DAL application.
I have:
public partial class ContainerContext : DbContext
{
public ContainerContext()
{
}
public ContainerContext(DbContextOptions<ContainerContext> options) : base(options)
{
}
}
In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var connection = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<ContainerContext>(options => options.UseSqlServer(connection));
services.AddDAL();
}
In the Dal project:
public static class ServiceCollectionExtensions
{
// Add parameters if required, e.g. for configuration
public static IServiceCollection AddDAL(this IServiceCollection services)
{
// Register all services as required
services.AddScoped<ILeaseBll, LeaseDal>();
return services;
}
}
The Dal class.
public class LeaseDal : ILeaseBll
{
private string conString;
public LeaseDal(???????)
{
// Some validation for the Context maybe (isNull etc?) throw new ArgumentNullException("conString");
//this.connectionString = conString;
}
How would / should it be done?
Thanks
There is a philosophy change with Dot-Net-Core and Dot-Net-Framework....
public class LeaseDal : ILeaseBll
{
private string conString;
This is not best practice in dot-net-CORE.
You do NOT inject your "connection string" in your concrete DataAccessLayer object.
You inject the db-context.
(and the db-context already has been wired to the Ioc...with its correct connection string)
Something like this:
public interface IDepartmentQueryDomainData()
{
Task<int> GetCountAsync(CancellationToken token);
}
..
public class DepartmentQueryEntityFrameworkDomainDataLayer : IDepartmentQueryDomainData
{
public const string ErrorMessageILoggerFactoryIsNull = "ILoggerFactory is null";
public const string ErrorMessageMyCoolDbContextIsNull =
"MyCoolDbContext is null";
private readonly ILogger<DepartmentQueryEntityFrameworkDomainDataLayer> logger;
private readonly MyCoolDbContext entityDbContext;
public DepartmentQueryEntityFrameworkDomainDataLayer(
ILoggerFactory loggerFactory,
MyCoolDbContext context
{
if (null == loggerFactory)
{
throw new ArgumentNullException(ErrorMessageILoggerFactoryIsNull, (Exception)null);
}
this.logger = loggerFactory.CreateLogger<DepartmentQueryEntityFrameworkDomainDataLayer>();
this.entityDbContext = context ?? throw new ArgumentNullException(
ErrorMessageMyCoolDbContextIsNull,
(Exception)null);
}
public async Task<int> GetCountAsync(CancellationToken token)
{
int returnValue = await this.entityDbContext.Departments.AsNoTracking().CountAsync(token);
this.logger.Log(
new LogEntry(
LoggingEventTypeEnum.Trace,
string.Format(
LogMessages.Count,
returnValue)));
return returnValue;
}
}
You can also "see" this here:
https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/
public class MyController
{
private readonly ApplicationDbContext _context;
public MyController(ApplicationDbContext context)
{
_context = context;
}
}
I would never inject the dbContext into a "controller"...(I agree with you that the Dal should be a separate layer)...
but besides that "miscue" on the microsoft example, you do see that you inject the dbContext.
Also see:
https://hovermind.com/aspnet-core/using-dbcontext-with-dependency-injection.html

Static file not being served from second project

I have 2 startup projects:
TB-GUI
TB-WPS
both are webprojects.
Each has a static file
TBGlobalSettings.cs
WPSGlobalSettings.cs
At the startup both are properly initialized. The problem is when I try to access WPSGlobalSettings.ToolboxConnString later from the controller it is null.
Controller.cs
using (var m_Connection = new
NpgsqlConnection(WPSGlobalSettings.ToolboxConnString.ConnectionString))
{
//...some code
}
TBGlobalSettings however is always available.
I have also installed package Microsoft.AspNetCore.StaticFiles in TBWPS project.
Any solution for this? (I am also interested in the why of this behaviour)
WPSGlobalSettings.cs
public static class WPSGlobalSettings
{
private static readonly string ToolboxDatabaseName = GetValue<string>("tbdatabasename", "db");
public static NpgsqlConnectionStringBuilder ToolboxConnString = build_conn_str(ToolboxDatabaseName);
private static IWebHostEnvironment _env;
internal static void Configure(IWebHostEnvironment env)
{
_env = env;
}
private static T GetValue<T>(string key, string prefix = Prefix)
{
string entry = string.Format("{0}:{1}", prefix, key);
var value = Startup.WPSStaticConfig.GetValue<string>(entry);
}
//...other code...
}
At startup these values are being filled/initialized.
When called from the controller, these values are null and the debugger goes to initialize them once more, therefore it crashes at the following line as WPSStaticConfig is also null
var value = Startup.WPSStaticConfig.GetValue<string>(entry);
Startup.cs (TB-WPS)
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
WPSStaticConfig = configuration;
}
public IConfiguration Configuration { get; }
public static IConfiguration WPSStaticConfig { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(Configuration);
services.AddControllersWithViews();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
WPSGlobalSettings.Configure(env);
...
}
TB-WPS is my secondary startup project where this issue is happening, the main mvc project (with UI) TB-GUI is implemented exactly same way and doesnt have any issues.
It may help if you could show your model and how you bind the connect string
you could try as below:
configuration.GetSection("xxxxx").Bind(WPSGlobalSettings.ToolboxConnString);
Model class:
public static class WPSGlobalSettings
{
public static ToolboxConnString ToolboxConnString { get; set; } = new ToolboxConnString();//need instanced
}
public class ToolboxConnString
{
public string ConnectionString{get;set;}
........
}

Accessing appsettings.json (in WepApi-project) in Class library project

I know this issue has been responded to before, but I cannot seem to get it to work in my case. The solution in Visual Studio contains several project, including WepApi-project (asp.net core), and a class library project. In the class library project I need to access some info in the appsettings.json, residing in the WebApi-project. I've tried to followed different tutorials, which has lead to the following so far:
Appsettings.json
{
"LoginSettings": {
"email": "test#test.com",
"password": "12345678"
}
}
Class modeling configuration (resides in class library project):
LoginSettings
public class LoginSettings
{
public string Email { get; set; }
public string Password { get; set; }
}
Startup.cs (WebApi-project):
public void ConfigureServices(IServiceCollection services)
{
var loginSettings = new LoginSettings
{
Email = Configuration["LoginSettings:email"],
Password = Configuration["LoginSettings:password"]
};
services.AddSingleton(loginSettings);
services.AddOptions();
}
Class in which I need the settings
public static class Test
{
private readonly LoginSettings settings;
public static async Task<string> GetTokenAsync()
{
var email = settings.Email;
...
}
}
I cannot get anything from LoginSettings. In addition, I have tried to make Test-class non-static in order to inject LoginSettings into the constructor. But when I then try to instantiate Test-class, it asks for an instance of LoginSettings. I don't really feel for making Test-class non-static, as it's only purpose is to return a string to another class, and it needs the LoginSettings to do so. What do I do wrong?
Regards.
EDIT
I have made the class Test non-static. What do I pass in when instatiating it?
In the Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<LoginSettings>(Configuration.GetSection("LoginSettings"));
}
In the controller:
public class HomeController : Controller
{
private string _email { get; set; }
private string _password { get; set; }
public HomeController(IOptions<LoginSettings> options)
{
var login = options.Value;
_email = login.Email;
_password = login.Password;
}
public IActionResult GetTokenAsync()
{
// _email: "test#test.com"
// _password: "12345678"
}
}
NOTE: I suggest you name the property name in the appsettings.json like this:
{
"LoginSettings": {
"Email": "test#test.com",
"Password": "12345678"
}
}
Use Email and Password instead of email and password.
UPDATE (for I'm not going to use the LoginSettings in a controller. I'm going to use it in a class in the class library project):
public class MyLib
{
private string _email { get; set; }
private string _password { get; set; }
public MyLib(IOptions<LoginSettings> options)
{
var login = options.Value;
_email = login.Email;
_password = login.Password;
}
public async Task<string> GetTokenAsync()
{
// _email: "test#test.com"
// _password: "12345678"
}
}
UPDATE 2
To use this library. You cannot use it like this:
var lib = new MyLib(???)
Since MyLib (or Test in your example) is a service, you pass it in another constructor before using:
public class AnotherClass
{
private MyLib _lib { get; set; }
public AnotherClass(MyLib lib)
{
_lib = lib;
}
public async Task UseIt()
{
var token = await _lib.GetTokenAsync();
}
}
Ok, I'm trying to explain how it works in my case.
In my ClassLibrary I have the following extension method
public static class MyClassLibraryExtensions
{
public static IServiceCollection AddMyClassLibrary(this IServiceCollection services, LoginSettings options)
{
services.AddSingleton<LoginSettings>(options);
return services;
}
}
then in Web.API project
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
var loginSettings = new LoginSettings
{
Email = Configuration["LoginSettings:email"],
Password = Configuration["LoginSettings:password"]
};
services.AddMyClassLibrary(loginSettings);
...
}
and use it
public class Test
{
private readonly LoginSettings _settings;
public Test(LoginSettings loginSettings)
{
_settings = loginSettings;
}
public static async Task<string> GetTokenAsync()
{
var email = _settings.Email;
...
}
}

WCF + Unity nested web.config problem

I'm trying to setup the following:
/WebApplication
web.config
tokenlogin.aspx
/Services
web.config
AccessTokenService.svc
I put my WCF Service + configuration in the /WebApplication/Services folder.
This still workes as expected.
Now my AccessTokenService, which resides in an other assembly, expects an interface in its constructor called IAccessTokenRepository (see all code samples below).
Because normally WCF only allows for parameter-less constructors, I extended WCF using a custom IInstanceProvider, IServiceBehavior and BehaviorExtensionElement so Unity could resolve this for me.
As I did with the WCF configuration, I also put the Unity configuration inside the web.config file which resides in the Services folder. This way I don't pollute my web.config in my web app root. But that doesn't seem to work. I'm unable to read the unity configuration section from the web.config in the services folder. The GetSection part of the follwing code returns null:
public class UnityBehaviorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(UnityServiceBehaviour); }
}
protected override object CreateBehavior()
{
UnityContainer unityContainer = new UnityContainer();
UnityConfigurationSection section = LoadUnitySection() as UnityConfigurationSection;
section.Containers.Default.Configure(unityContainer);
return new UnityServiceBehaviour(unityContainer);
}
private object LoadUnitySection()
{
if (System.Web.Hosting.HostingEnvironment.IsHosted)
return WebConfigurationManager.GetSection("unity");
return ConfigurationManager.GetSection("unity");
}
}
Now if I move the unity configuration to the web.config int the root of the WebApplication everything works fine. And I have no idea why.
The rest of the code:
[ServiceContract(Namespace = "http://services.xxx.com/AccessTokenService/1.0")]
public interface IAccessTokenService
{
[OperationContract]
Guid RequestAccessToken(AccessTokenRequest accessTokenRequest);
}
public class AccessTokenService : IAccessTokenService
{
private readonly IAccessTokenRepository accessTokenRepository;
public AccessTokenService(IAccessTokenRepository accessTokenRepository)
{
if (accessTokenRepository == null)
throw new ArgumentNullException("accessTokenRepository");
this.accessTokenRepository = accessTokenRepository;
}
public Guid RequestAccessToken(AccessTokenRequest accessTokenRequest)
{
return accessTokenRepository.Store(accessTokenRequest);
}
}
public class UnityInstanceProvider : IInstanceProvider
{
private readonly Type serviceType;
private readonly UnityContainer unityContainer;
public UnityInstanceProvider(Type serviceType, UnityContainer unityContainer)
{
this.serviceType = serviceType;
this.unityContainer = unityContainer;
}
#region IInstanceProvider Members
public object GetInstance(InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
{
return unityContainer.Resolve(serviceType);
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
if (instance == null)
return;
if (instance is IDisposable)
((IDisposable)instance).Dispose();
}
#endregion
}
public class UnityServiceBehaviour : IServiceBehavior
{
private readonly UnityContainer unityContainer;
public UnityServiceBehaviour(UnityContainer unityContainer)
{
this.unityContainer = unityContainer;
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (var channel in serviceHostBase.ChannelDispatchers.OfType())
{
if (channel == null)
continue;
foreach (var endPointDispatcher in channel.Endpoints)
{
endPointDispatcher.DispatchRuntime.InstanceProvider =
new UnityInstanceProvider(serviceDescription.ServiceType, unityContainer);
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}

Resources