Static file not being served from second project - global-variables

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;}
........
}

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>();

Migration to .net6, problem with settings

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

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

How to do UnitOfWork + Repository patterns with entity framework core and unit testing

I'm running into an issue while unit testing where if I run multiple tests at once, the DbContext will lose track of records I've added during unit tests and I think this may have to do with how services are registered in my ServiceCollection.
I have the following setup:
IUnitOfWork:
public interface IUnitOfWork : IDisposable
{
IUserRepository Users { get; }
int Complete();
}
UnitOfWork
public class UnitOfWork : IUnitOfWork
{
private readonly MyDbContext _context;
public IUserRepository Users { get; }
public UnitOfWork(MyDbContext context,
IUserRepository userRepository)
{
_context = context;
Users = userRepository;
}
public void Dispose() => _context.Dispose();
public int Complete() => _context.SaveChanges();
}
UserRepository
public class UserRepository : Repository<User>, IUserRepository
{
public UserRepository(MyDbContext context) : base(context) { }
public MyDbContext MyDbContext => Context as MyDbContext;
public Task<User?> GetUserDetailsAsync(int userID)
{
var user = MyDbContext.Users.Where(user => user.Id == userID)
.Include(user => user.Emails)
.Include(user => user.PhoneNumbers).FirstOrDefault();
return Task.FromResult(user);
}
}
Here is my base test:
public abstract class BaseTest : IDisposable
{
protected ServiceProvider ServiceProvider { get; }
private MyDbContext MyDbContext { get; }
protected IUnitOfWork UnitOfWork { get; }
public BaseTest()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddScoped<IUserService, UserService>()
.AddScoped<IUnitOfWork, UnitOfWork>()
.AddScoped(typeof(IRepository<>), typeof(Repository<>))
.AddScoped<IOrganizationRepository, OrganizationRepository>()
.AddScoped<IExercisePostRepository, ExercisePostRepository>()
.AddScoped<IUserRepository, UserRepository>()
.AddTransient<IRestClient, RestClient>()
.AddAutoMapper(typeof(Startup).Assembly)
.AddDbContext<MyDbContext>(options =>
options.UseInMemoryDatabase("Core")
.EnableSensitiveDataLogging());
ServiceProvider = serviceCollection.BuildServiceProvider();
SpotcheckrCoreContext = ServiceProvider.GetRequiredService<MyDbContext>();
MyDbContext.Database.EnsureCreated();
UnitOfWork = ServiceProvider.GetRequiredService<IUnitOfWork>();
}
public void Dispose()
{
MyDbContext.Database.EnsureDeleted();
UnitOfWork.Dispose();
}
}
Sample test:
public class UserServiceTests : BaseTest
{
private readonly IUnitOfWork UnitOfWork;
private readonly IUserService Service;
public UserServiceTests()
{
UnitOfWork = ServiceProvider.GetRequiredService<IUnitOfWork>();
Service = ServiceProvider.GetRequiredService<IUserService>();
}
[Fact]
public async void GetUserAsync_WithValidUser_ReturnsUser()
{
var user = new User
{
FirstName = "John",
LastName = "Doe"
};
UnitOfWork.Users.Add(user);
UnitOfWork.Complete();
var result = await Service.GetUserAsync(user.Id);
Assert.Equal(user.Id, result.Id);
}
}
If I run this test by itself, then it will correctly pass and I can see the user in the repository. However if I run it with other tests and debug, then that user is lost once I inspect UnitOfWork.Users in the repository but I do see it in the UnitOfWork.Users in the test.
What is the correct approach here?
Edit 1:
Tried some other changes but no luck yet. Adjusted UnitOfWork to take in the interfaces of each repository and registering them in BaseTest as scoped services. Also tried marking BaseTest as implementing IDisposable and then executing:
public void Dispose()
{
MyDbContext.Database.EnsureDeleted();
UnitOfWork.Dispose();
}
In the service layer I'll see the Users just fine but as soon as I step into the repository layer I'll lose the Users :/ I have a suspicion it is related to dependency injection AddScoped vs AddTransient and how all of that works with running multiple unit tests.
Edit 2:
Tried some more things...Used IClassFixture<BaseTest> on each test class and then ensured that each test class implemented IDisposable and in there I would ensure the Context database was deleted; also ensured in the test class constructor that it was created. With this I ended up with the following error:
The instance of entity type cannot be tracked because another instance with the same key value for {'Id'} is already being tracked
And so I added .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking) but the problem still persisted.
This is very annoying to setup.
This is what has resolved it for me for now.
Summary: Created a new ServiceFixture. This ServiceFixture is applied to a BaseTest class as IClassFixture<ServiceFixture>. The ServiceFixture is responsible for initializing the service collection and allowing for it to be reused across different test classes. The purpose of the BaseTest is to allow for disposal of the database and other clean up that is necessary after each test. The Dispose method of this class will detach entity state and also delete the database.
ServiceFixture.cs
public class ServiceFixture
{
public ServiceProvider ServiceProvider { get; }
public ServiceFixture()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddScoped<IUserService, UserService>()
.AddScoped<ICertificationService, CertificationService>()
.AddScoped<IOrganizationService, OrganizationService>()
.AddScoped<ICertificateService, CertificateService>()
.AddScoped<IUnitOfWork, UnitOfWork>()
.AddScoped<IUserRepository, UserRepository>()
.AddScoped<IExercisePostRepository, ExercisePostRepository>()
.AddScoped<IEmailRepository, EmailRepository>()
.AddScoped<IPhoneNumberRepository, PhoneNumberRepository>()
.AddScoped<ICertificationRepository, CertificationRepository>()
.AddScoped<ICertificateRepository, CertificateRepository>()
.AddScoped<IOrganizationRepository, OrganizationRepository>()
.AddTransient<IRestClient, RestClient>()
.AddSingleton<NASMCertificationValidator>()
.AddAutoMapper(typeof(Startup).Assembly)
.AddDbContext<SpotcheckrCoreContext>(options =>
options.UseInMemoryDatabase("Spotcheckr-Core")
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.EnableSensitiveDataLogging());
ServiceProvider = serviceCollection.BuildServiceProvider();
}
}
BaseTest.cs
public abstract class BaseTest : IClassFixture<ServiceFixture>, IDisposable
{
protected readonly ServiceProvider ServiceProvider;
protected readonly IUnitOfWork UnitOfWork;
private readonly SpotcheckrCoreContext Context;
public BaseTest(ServiceFixture serviceFixture)
{
ServiceProvider = serviceFixture.ServiceProvider;
Context = serviceFixture.ServiceProvider.GetRequiredService<SpotcheckrCoreContext>();
UnitOfWork = serviceFixture.ServiceProvider.GetRequiredService<IUnitOfWork>();
Context.Database.EnsureCreated();
}
public void Dispose()
{
Context.ChangeTracker.Entries().ToList().ForEach(entry => entry.State = EntityState.Detached);
Context.Database.EnsureDeleted();
}
}
UserServiceTests.cs
public class UserServiceTests : BaseTest
{
private readonly IUserService Service;
public UserServiceTests(ServiceFixture serviceFixture) : base(serviceFixture)
{
Service = serviceFixture.ServiceProvider.GetRequiredService<IUserService>();
}
[Fact]
public async void GetUserAsync_WithInvalidUser_ThrowsException()
{
Assert.ThrowsAsync<InvalidOperationException>(() => Service.GetUserAsync(-1));
}
[Fact]
public void CreateUser_UserTypeAthlete_CreatesAthleteUser()
{
var result = Service.CreateUser(Models.UserType.Athlete);
Assert.IsType<Athlete>(result);
}
[Fact]
public void CreateUser_UserTypePersonalTrainer_CreatesPersonalTrainerUser()
{
var result = Service.CreateUser(Models.UserType.PersonalTrainer);
Assert.IsType<PersonalTrainer>(result);
}
}

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