I'm working on an intranet MVC web application, using Fluent NHibernate.
As everyone knows, creating the necessary ISessionFactory is heavy, and therefore should be done only once. Hence, I create it in the Global.asax file during Application_Start, then cache it in the application for future use.
The problem is that I only want to give access to users who already have permissions over the database.
This could theoretically be solved by defining Integrated Security=SSPI in the connection string (rather than by providing an SQL username and password).
However, this raises an error during Fluently.Configure, because the configuration occurs during Application_Start, which is being run by the process hosting the application, which does not have permissions to connect to the DB.
How do I solve this issue?
You could initialize it in BeginRequest instead of Application_Start:
private static bool _initialized = false;
private static object _syncRoot = new object();
protected void Application_BeginRequest(object source, EventArgs e)
{
if (_initialized)
{
return;
}
lock (_initialized)
{
if (_initialized)
{
return;
}
// Initialize the session factory here and cache it
_initialized = true;
}
}
Related
I am working on my first Blazor Server application, which is also my first Entity Framework Core application. I am wanting to set up a background service which, once a day in the early morning, checks the database to see if any of a certain record type has been added with yesterday's date. If so, the relevant records are pulled, formatted, and then emailed to a stakeholder.
I have the EF, formatting, and emailing code working just fine when I trigger the report by manually visiting the page. The problem that I have is how to provide the background service with a DbContextFactory so that the EF and related code can execute.
Up to this point I've always used Razor-based dependency injection to inject the IDbContextFactory via an inject IDbContextFactory<OurAppContext> DbFactory at the top of the page, and then accessed the DbFactory via the DbFactory variable.
However, background services are (according to this Microsoft tutorial) set up through Program.cs, so I don't have access to Razor-based dependency injection there.
I have set up my background service (what I call the PhaseChangeReportService) as indicated in the above link, and it dutifully outputs to the console every 10 seconds that it is running with an updated execution count. I don't fully understand what's going on with the various layers of indirection, but it appears to be working as Microsoft intended.
I noted that the constructor for the background service takes in an ILogger as a parameter, specifically:
namespace miniDARTS.ScopedService
{
public sealed class PhaseChangeReportService : IScopedProcessingService
{
private int _executionCount;
private readonly ILogger<PhaseChangeReportService> _logger;
public PhaseChangeReportService(ILogger<PhaseChangeReportService> logger)
{
_logger = logger;
}
public async Task DoWorkAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
++_executionCount;
_logger.LogInformation("{ServiceName} working, execution count: {Count}", nameof(PhaseChangeReportService), _executionCount);
await Task.Delay(10_000, stoppingToken);
}
}
}
}
I was (and am) confused that the constructor is never referenced within Visual Studio, but when I drop a breakpoint on its one line of code it is hit. I tried modifying this constructor's signature so that it took in an IDbFactory<OurAppContext> as well, so that whatever dark magic is allowing an ILogger<BackgroundServiceType> to come in for assignment to _logger might bring in a DbFactory<OurAppContext> as well, like so:
private readonly ILogger<PhaseChangeReportService> _logger;
private readonly IDbContextFactory<miniDARTSContext> _dbContextFactory;
public PhaseChangeReportService(ILogger<PhaseChangeReportService> logger, IDbContextFactory<miniDARTSContext> dbContextFactory)
{
_logger = logger;
_dbContextFactory = dbContextFactory;
}
However, doing so just led to the constructor breakpoint being skipped over and not breaking, with no exception being thrown or any console output of any kind (i.e. the prior execution count console output no longer showed up). So, I gave up on that approach.
Here is the relevant section of Program.cs:
// Configure the database connection.
string connectionString = builder.Configuration.GetConnectionString("miniDARTSContext");
var serverVersion = new MySqlServerVersion(new Version(8, 0, 28));
builder.Services.AddDbContextFactory<miniDARTSContext>(options => options.UseMySql(connectionString, serverVersion), ServiceLifetime.Scoped);
IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddHostedService<ScopedBackgroundService>();
services.AddScoped<IScopedProcessingService, PhaseChangeReportService>();
})
.Build();
host.RunAsync();
Here's IScopedProcessingService.cs:
namespace miniDARTS.ScopedService
{
public interface IScopedProcessingService
{
Task DoWorkAsync(CancellationToken stoppingToken);
}
}
And here's ScopedBackgroundService.cs:
namespace miniDARTS.ScopedService;
public sealed class ScopedBackgroundService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ScopedBackgroundService> _logger;
public ScopedBackgroundService(IServiceProvider serviceProvider, ILogger<ScopedBackgroundService> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation($"{nameof(ScopedBackgroundService)} is running.");
await DoWorkAsync(stoppingToken);
}
private async Task DoWorkAsync(CancellationToken stoppingToken)
{
_logger.LogInformation($"{nameof(ScopedBackgroundService)} is working.");
using (IServiceScope scope = _serviceProvider.CreateScope())
{
IScopedProcessingService scopedProcessingService = scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();
await scopedProcessingService.DoWorkAsync(stoppingToken);
}
}
public override async Task StopAsync(CancellationToken stoppingToken)
{
_logger.LogInformation($"{nameof(ScopedBackgroundService)} is stopping.");
await base.StopAsync(stoppingToken);
}
}
I'm confident I'm misunderstanding something relatively fundamental here when it comes to services / dependency injection, but my Googling and review of past StackOverflow answers has not turned up anything I can run with.
The IDbContextFactory is an interface that is used for creating instances of a DbContext. When you add it to your services on program.cs for Blazor (services.AddDbContextFactory(parameters)), it implements the IDbContextFactory for you. This allows you to use the #inject IDbContextFactory<YourDbContext> DbFactory at the top of your razor components and then within your code you can call the CreateDbContext method when you need to create an instance of the DbContext (ex. using var context = DbFactory.CreateDbContext()).
You can pass an injected DbContextFactory as a parameter from a razor component to a class, and then use that DbContextFactory in a method to create an instance of the DbContext (see constructor injection), but that still relies on the razor component to inject the DbContextFactory to begin with.
To create an instance of a DbContext independent of a razor component, you need to use the constructor for your DbContext. Your DbContext will have a public constructor with a DbContextOptions parameter (this is required to be able to use AddDbContextFactory when registering the factory service in program.cs). You can use this constructor to implement your own factory. If you aren't sure which options to use, you can check your program.cs to see what options you used there.
public class YourDbFactory : IDbContextFactory<YourDbContext>
{
public YourDbContext CreateDbContext()
{
var optionsBuilder = new DbContextOptionsBuilder<YourDbContext>();
optionsBuilder.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=Test"));
return new YourDbContext(optionsBuilder);
}
}
Once you've created your own implementation of the IDbContextFactory interface, you can then use it in your code independent of razor components - for example in the background service class.
YourDbFactory DbFactory = new YourDbFactory();
using var context = DbFactory.CreateDbContext();
Background:
.NET 6 application (front-end Angular SPA) Will be deployed as single application with 1 database per tenant. There is a shared database (called GlobalContext) which holds Tenant and TenantUser information.
In my Program.cs I don't have a connection string to the tenant database, as that information information is only available after a user has logged in.
builder.Services.AddDbContext<GlobalContext>(
options => options.UseSqlServer(config.GetValue<string>("ConnectionStringGlobal"))
);
No connection string specified here
builder.Services.AddDbContext<FinanceAppContext>();
In the OnConfiguring method in the FinanceappContext I obtain the connection string using a service;
var tenant = await _tenantService.GetConnectionString();
optionsBuilder.UseSqlServer(_config.GetValue<string>(tenant));
base.OnConfiguring(optionsBuilder);
The TenantService is a transient service which obtains the logged in users tenant from the GlobalContext
public async Task<string> GetConnectionString() {
try
{
var userId = _contextAccessor.HttpContext.User.FindFirst(ClaimTypes.Email).Value;
var user = await _globalContext.TenantUser
.Include(tenantuser => tenantuser.Tenant)
.FirstOrDefaultAsync(tenantuser => tenantuser.EmailAddress == userId);
return user.Tenant.Name;
}
catch (System.Exception)
{
return "";
}
}
But when I try to access any data via the FinanceAppContext I get the following error;
A relational store has been configured without specifying either the DbConnection or connection string to use
It seems like the fact I don't specify a connection string in Program.cs and but do specify one in OnConfiguring seems to be an issue?
So the issue was that my OnConfiguring method was marked as async and I was making an await call to my TenantService to obtain the connection string. I remove the async/await and the retrieval of the user from the GlobalContext now works.
I'm currently working on WPF app that is architectured as follows:
MVVM
Entity Framwork 4 (with LINQ).
WCF service that pool Database to get data (Oracle).
I Make my WFC call in my View Model Class and put my data in an ObsevableCollections.
Db Changes occurs from a another app.
So my app does not do any write actions on the DB what-so-ever (Zéro), it only reads data and displays it on the UI.
How can I make my app to be quickly responsive to DB changes, I read about the following solutions but I'm confused and don't know what to use:
Pooling DB every n seconds with a DispatcherTimer (seems to be too much work cause data changes every millisecond)
SqlDependency, searched all over the internet but didn't find a proper implementation with EF.
As I said, db changes every millisecond (financial data from other sources),
How can resolve this?
Thank you.
i tried the code bellow and it seemed to be working okay for the moment (but i still have some doubts about that infinit loop), let me know what you thing :
public class MyViewModel
{
BackgroundWorker _bgWorker ;
//some props
//some funcs
protected internal MyViewModel(Session session)
{
Session = session;
RefreshData();
}
protected void RefreshData()
{
try
{
_bgWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_bgWorker.DoWork += bgDoWork;
if (!_bgWorker.IsBusy)
{
_bgWorker.RunWorkerAsync();
}
}
catch (Exception)
{
_bgWorker.CancelAsync();
}
}
private void bgDoWork(object sender, DoWorkEventArgs e)
{
var worker = (BackgroundWorker)sender;
while (!worker.CancellationPending)
{
//Thread.Sleep(1000); should i keep this or not ?
Proxy(); // WCF calls
}
}
}
I have a Windows service that is designed for Dependency Injection. The service runs several threaded timers, each of which polls a database and sometimes processes records in it.
One of the dependencies injected into the service is a Repository object which gives the required access to the database. The repository in turn encapsulates an Entity Framework DbContext.
Currently, an instance of the repository is injected when the service starts and remains available until it stops. I don't like this for two reasons:
The DbContext lies idle most of the time but keeps a database connection open
The DbContext becomes stale; external changes to the data already read into it are not reflected
I would like to change the way the repository is managed so that each invocation of a timer's execute method gets a fresh repository instance while keeping the repository as an injectable service.
My preferred approach is to inject a RepositoryFactory object rather than the repository so that the timer execute method can create its own repository. However, although the repository instance is created successfully, and the first access to the database through it (a GET) succeeds, the next database access (another GET contained within an UPDATE method) fails as the DbContext appears to have been disposed.
Can anyone cast any light on why this is happening, and suggest a solution? I'm open to using a pattern other than Factory if it meets the instantiation requirements.
//DI configuration
public static void AddRepository(this IServiceCollection services, string connectionString)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(connectionString));
services.AddScoped<IRepository, Repository>();
services.AddScoped<IRepositoryFactory, RepositoryFactory>(Factory);
}
private static readonly Func<IServiceProvider, RepositoryFactory> Factory = (_serviceProvider) =>
{
Console.WriteLine("ServiceProvider instantiated: {0}", _serviceProvider != null);
return new RepositoryFactory(_serviceProvider);
};
//Windows service constructor
public AllocationService(IRepositoryFactory factory, IMapperConfiguration mapperConfig)
{
_repositoryFactory = factory;
_mapperConfig = mapperConfig as MapperConfiguration;
InitializeComponent();
}
//Timer execution method
private void _registrationTimer_Elapsed(object stateObject)
{
EventLog.WriteEntry(Constants.AllocationSourceName, string.Format("Registration Timer Elapsed on thread {0}", Thread.CurrentThread.ManagedThreadId), EventLogEntryType.Information, (int)UseCaseType.Register);
try
{
using (var repository = _repositoryFactory.Create())
{
//query the database for Approved Assets,
var list = repository.GetRegistrations(Status.Approved);
EventLog.WriteEntry(Constants.AllocationSourceName, string.Format("{0} registrations with status=Approved", list.Count()), EventLogEntryType.Information, (int)UseCaseType.Register);
foreach (var registration in list)
{
try
{
//some processing removed here for simplicity
//update database
registration.Status = Status.Allocated;
//exception thrown here (see below)
repository.UpdateRegistration(registration);
}
catch (Exception ex)
{
//Log any problems and continue
EventLog.WriteEntry(Constants.AllocationSourceName, ex.ToString(), EventLogEntryType.Error, (int)UseCaseType.Register);
}
}
}
}
catch (Exception ex)
{
//Log any problems on saving
EventLog.WriteEntry(Constants.AllocationSourceName, ex.ToString(), EventLogEntryType.Error, (int)UseCaseType.Register);
}
}
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'MyDbContext'.
at Microsoft.Data.Entity.DbContext.get_ServiceProvider()
at Microsoft.Data.Entity.DbContext.Microsoft.Data.Entity.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
at Microsoft.Data.Entity.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.Data.Entity.Internal.InternalDbSet`1.<.ctor>b__2_0()
at Microsoft.Data.Entity.Internal.LazyRef`1.get_Value()
at Microsoft.Data.Entity.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.Any[TSource](IQueryable`1 source, Expression`1 predicate)
at RegistryApp.Repository.Repository.ProcessAsset(Asset asset) in C:\svn\Client Applications\ITC\RegistryApp\RegistryApp.Repository\Repository.cs:line 364
at RegistryApp.Repository.Repository.UpdateAsset(Asset asset) in C:\svn\Client Applications\ITC\RegistryApp\RegistryApp.Repository\Repository.cs:line 270
I have implemented a CurrentUserPropertyBinder (see below) for a web application using FubuMVC.
public class CurrentUserPropertyBinder : IPropertyBinder
{
private readonly Database _database;
private readonly ISecurityContext _security;
public CurrentUserPropertyBinder(Database database, ISecurityContext security)
{
_database = database;
_security = security;
}
public bool Matches(PropertyInfo property)
{
return property.PropertyType == typeof(User)
&& property.Name == "CurrentUser";
}
public void Bind(PropertyInfo property, IBindingContext context)
{
var currentUser = //check database passing the username to get further user details using _security.CurrentIdentity.Name
property.SetValue(context.Object, currentUser, null);
}
}
When I login to my site, this works fine. The CurrentUserPropertyBinder has all the information it requires to perform the task (i.e. _security.CurrentIdentity.Name has the correct User details in it)
When I try and import a file using fineUploader (http://fineuploader.com/) which opens the standard fileDialog the _security.CurrentIdentity.Name is empty.
It doesn't seem to remember who the user was, I have no idea why. It works for all my other routes but then I import a file it will not remember the user.
Please help! Thanks in Advance
NOTE: We are using FubuMVC.Authentication to authenticate the users
I'm guessing your action for this is excluded from authentication; perhaps it's an AJAX-only endpoint/action. Without seeing what that action looks like, I think you can get away with a simple fix for this, if you've updated FubuMVC.Authentication in the past 3 months or so.
You need to enable pass-through authentication for this action. Out of the box, FubuMVC.Auth only wires up the IPrincipal for actions that require authentication. If you want access to that information from other actions, you have to enable the pass-through filter. Here are some quick ways to do that.
Adorn your endpoint/controller class, this specific action method, or the input model for this action with the [PassThroughAuthentication] attribute to opt-in to pass-through auth.
[PassThroughAuthentication]
public AjaxContinuation post_upload_file(UploadInputModel input) { ... }
or
[PassThroughAuthentication]
public class UploadInputModel { ... }
Alter the AuthenticationSettings to match the action call for pass-through in your FubuRegistry during bootstrap.
...
AlterSettings<AuthenticationSettings>(x => {
// Persistent cookie lasts 3 days ("remember me").
x.ExpireInMinutes = 4320;
// Many ways to filter here.
x.PassThroughChains.InputTypeIs<UploadInputModel>();
});
Check /_fubu/endpoints to ensure that the chain with your action call has the pass-through or authentication filter applied.