How do you construct Entity Framework Core IdentityDbContext<> outside of a service? - entity-framework

I was curious as generally you can do this WITHOUT IdentityDbContext:
public class SomeContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
//I am using Postgres but will also accept answers to SQL Server as I want both ultimately
optionsBuilder.UseNpgsql(#"Host=localhost;Database=someDb;Username=user;Password=yeah");
}
}
}
But when I go to the 'IdentityDbContext<(POCOUser)>' my OnConfiguring method disappears. And it appears this is really not wired up anymore except if I am starting up a service and doing something similar to the Startup of:
services.AddDbContext<SomeContext>(cfg =>
{
cfg.UseSqlServer(_config.GetConnectionString("MyConnectionString"));
});
That's great if I am doing a service and the EF Core is in the same contained project. But what if I am not? Are you not able to specify the connection string on the fly in a constructor or other method on instantiation?

appsettings.json - add connection string:
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SomeDatabase": "Host=localhost;Database=...;Username=...;Password=..."
}
}
Update Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);
services
.AddEntityFrameworkNpgsql()
.AddDbContext<SomeContext>(options => options.UseNpgsql(Configuration.GetConnectionString("SomeDatabase")))
.BuildServiceProvider();
}
Inject into controller:
private SomeContext _db;
public SomeController(SomeContext someContext)
{
_db = someContext;
}

Related

Blazor Server (EF Core) and database connection

Sorry for the rudimentary question.
I'm currently studying application development with Blazor Server and am having trouble connecting to a database.
I'm trying to use DI to connect to the database.
I created a code that uses the factory pattern as shown below, but an error occurs in the part that gets the connection string.
public void ConfigureServices(IServiceCollection services)
{
//error:CS0121 Inappropriate call between the following methods or properties: 'Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.AddDbContextFactory<TContext>(Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Action<Microsoft.EntityFrameworkCore.DbContextOptionsBuilder>, Microsoft.Extensions.DependencyInjection.ServiceLifetime)' と 'BlazorSv.Models.FactoryExtensions.AddDbContextFactory<TContext>(Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Action<Microsoft.EntityFrameworkCore.DbContextOptionsBuilder>, Microsoft.Extensions.DependencyInjection.ServiceLifetime)'
services.AddDbContextFactory<BlazorSv.Models.SQLbeginnerContext>(options => options.UseNpgsql(Configuration.GetConnectionString("DBConnection")));
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
I thought that the definition of the <Models.SQLbeginnerContext> part was ambiguous, so I wrote the hierarchy, but it didn't work.
What should I do about this error?
I want some advice
Below is SQLbegginerContext.cs that describes StartUp.cs and the factory pattern.
StartUp.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
//error:CS0121 Inappropriate call between the following methods or properties: 'Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.AddDbContextFactory<TContext>(Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Action<Microsoft.EntityFrameworkCore.DbContextOptionsBuilder>, Microsoft.Extensions.DependencyInjection.ServiceLifetime)' と 'BlazorSv.Models.FactoryExtensions.AddDbContextFactory<TContext>(Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Action<Microsoft.EntityFrameworkCore.DbContextOptionsBuilder>, Microsoft.Extensions.DependencyInjection.ServiceLifetime)'
services.AddDbContextFactory<Models.SQLbeginnerContext>(options => options.UseNpgsql(Configuration.GetConnectionString("DBConnection")));
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
SQLbegginerContext.cs
public interface IDbContextFactory<TContext> where TContext : DbContext
{
TContext CreateDbContext();
}
public class blazordbFactory<TContext> : IDbContextFactory<TContext> where TContext : DbContext
{
public blazordbFactory(IServiceProvider provider)
{
this.provider = provider;
}
private readonly IServiceProvider provider;
public TContext CreateDbContext()
{
return ActivatorUtilities.CreateInstance<TContext>(provider);
}
}
public static class FactoryExtensions
{
public static IServiceCollection AddDbContextFactory<TContext>(
this IServiceCollection collection,
Action<DbContextOptionsBuilder> optionsAction = null,
ServiceLifetime contextAndOptionsLifetime = ServiceLifetime.Singleton)
where TContext : DbContext
{
collection.Add(new ServiceDescriptor(
typeof(IDbContextFactory<TContext>),
sp => new blazordbFactory<TContext>(sp),
contextAndOptionsLifetime));
collection.Add(new ServiceDescriptor(
typeof(DbContextOptions<TContext>),
sp => GetOptions<TContext>(optionsAction, sp),
contextAndOptionsLifetime));
return collection;
}
private static DbContextOptions<TContext> GetOptions<TContext>(
Action<DbContextOptionsBuilder> action,
IServiceProvider sp = null) where TContext : DbContext
{
var optionsBuilder = new DbContextOptionsBuilder<TContext>();
if (sp != null)
{
optionsBuilder.UseApplicationServiceProvider(sp);
}
action?.Invoke(optionsBuilder);
return optionsBuilder.Options;
}
}
public partial class SQLbeginnerContext : DbContext
{
public SQLbeginnerContext()
{
}
public SQLbeginnerContext(DbContextOptions<SQLbeginnerContext> options)
: base(options)
{
}
追記:SQLbegginer.cs
//Comment out this part
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseNpgsql("Host=localhost; Database=SQLbeginner; Username=user; Password=****")
}
}

Adding controller with Entity Framework failed - DbContext Error

I have a .Net Core app with EntityFramework.
I wanted to add controller with CRUD operations.
But I got this error
I don't get any error while performing add-migration. It finds only one DbContext.
These are all the places where I have WebApplication8Context
WEBAPPLICATION8CONTEXT.CS
namespace WebApplication8.Data
{
public class WebApplication8Context : IdentityDbContext<ApplicationUser>
{
public WebApplication8Context(DbContextOptions<WebApplication8Context> options)
: base(options)
{
}
public DbSet<Tasks> Tasks { get; set; }
public DbSet<TaskGroups> TaskGroups { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
IDENTITYHOSTINGSTARTUP.CS
[assembly: HostingStartup(typeof(WebApplication8.Areas.Identity.IdentityHostingStartup))]
namespace WebApplication8.Areas.Identity
{
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) => {
services.AddDbContext<WebApplication8Context>(options =>
options.UseSqlServer(
context.Configuration.GetConnectionString("WebApplication8ContextConnection")));
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<WebApplication8Context>();
});
}
}
}
APPSETTINGS.JSON
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"WebApplication8ContextConnection": "Server=CREATIVESM\\SQLEXPRESS;Database=MarApplication;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
BEGGINING OF MY WEBAPPLICATION8MODELSNAPSHOT
namespace WebApplication8.Migrations
{
[DbContext(typeof(WebApplication8Context))]
partial class WebApplication8ContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "3.1.10")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
My Startup.cs
namespace WebApplication8
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}
}
Other places where I can find WebApplication8Context are my migrations.
What am I doing wrong?
It looks like there is a conflict between IdentityHostingStartup and Startup.
Although I don't know the specific reason, you can use the following methods to solve it:
You can delete the code in your IdentityHostingStartup,then add it to your Startup like this:
services.AddDbContext<WebApplication8Context>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("WebApplication8ContextConnection")));
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<WebApplication8Context>();
services.AddControllersWithViews();
services.AddRazorPages();
Re-migration and update database,then you will add the controller successful.

Clients.All, Clients.Caller and Clients.Others throwing TypeLoadException

I have a simple Typed Hub setup through an interface. I can successfully connect to the SignalR Hub.
namespace POC.WebSocket
{
public class ApiResultHub : Hub<IApiResultHubClient>
{
public async Task SendMessage(string user, string message)
{
await Clients.All.ReceiveMessage(user, message);
}
public override Task OnConnectedAsync()
{
Clients.Caller.ReceiveMessage("newUser", $"{Context.ConnectionId}");
return base.OnConnectedAsync();
}
}
}
However, when I try invoke a client side hub method it throws the following exception:
Here are my other class files:
Startup.cs
namespace POC.WebSocket
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.WithOrigins("https://localhost:44324", "https://localhost:44326")
.AllowCredentials();
}));
services.AddSignalR();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors("CorsPolicy");
app.UseSignalR(routes =>
{
routes.MapHub<ApiResultHub>("/apiResult");
});
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
IApiResultHubClient`T.cs
namespace POC.Common
{
public interface IApiResultHubClient
{
Task ReceiveMessage(string user, string message);
Task SendApiResponse<T>(ApiResult<T> data);
}
}
Has anyone else seen this issue? I saw few similar issues with being able to connect to the hub itself. However, as mentioned I am able to successfully connect to the hub, just not able to resolve Clients.All, Clients.Caller or Clients.Others.
All libraries updated today:
.Net Core version 2.1.4
.Net SignalR Core version 1.0.3
Edits:
Found details of the exception:
Method 'SendApiResponse' in type
'Microsoft.AspNetCore.SignalR.TypedClientBuilder.IApiResultHubClientImpl'
from assembly 'Microsoft.AspNetCore.SignalR.TypedClientBuilder,
Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have
an implementation.

ASP.NET Core/EF Core 2.0 using too much memory that results SQL Server Timeouts

tl;dr I am not cleaning up the context, as I assume that is all handled with DI/automatically being scoped (this might be my problem)
I have a ASP.NET Core that utilizes EF Core and after a while my site being live, the memory on the dotnet process for the app shoots to 1GB, at that point I see a lot of SQL Timeout errors. If I kill the process then those errors go away after the process re-spawns on a request for a while and it repeats..
I suspect somewhere in my code I have a memory leak or my design is not correct that results in something not cleaning up properly.
Here is my setup.
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
var connectionString = Configuration.GetSection("GeneralSettings:ConnectionString").Value;
services.AddDbContext<CryptoCalContext>(options => options.UseSqlServer(connectionString));
...
services.AddScoped<IDbService, DbService>();
}
I pass the context to a service(DbService) handles all my DB calls
public class DbService : IDbService
{
public CryptoCalContext DbContext { get; set; }
public DbService(CryptoCalContext context, ...)
{
DbContext = context;
...
}
public List<CategoryDto> Categories
{
get
{
return MemoryCache.GetOrCreate("Categories", entry =>
{
entry.SlidingExpiration = TimeSpan.FromMinutes(GeneralSettings.CacheExperationMinutes);
return DbContext.Categories.Include(x => x.Articles).Select(x => new CategoryDto(x)).ToList();
});
}
}
}
And in my calls to db, I do not make use of "using blocks" or call dispose.
I assume each call creates a separate db connection when needed so I do not do any of that (maybe this is where I am going wrong)
public class HomeController : BaseController
{
public HomeController(IDbService dbService ,..)
: base(dbService, ...)
{
}
}
public class BaseController : Controller
{
public IDbService DbService { get; set; }
public BaseController(IDbService dbService, ...)
{
DbService = dbService;
}
}
In my controllers, I simply do DbService.Categories, one of many different calls in my DbService, I put this example to illustrate that I do zero cleaning
EDIT:
public class CryptoCalContext : DbContext
{
public CryptoCalContext(DbContextOptions<CryptoCalContext> options)
: base(options)
{
}
public CryptoCalContext()
{
}
...Properties...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
}

Configure DbContext for OpenIddict

I'm using OpenIddict for JWT token authentication in my .NET Core app. I've followed this tutorial but I am now receiving the following error:
InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider...
My ConfigureServices method in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
services.AddEntityFrameworkSqlServer()
.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(Configuration["Data:MyDbContext:ConnectionString"]));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<MyDbContext>()
.AddDefaultTokenProviders()
.AddOpenIddictCore<Application>(config => config.UseEntityFramework());
services.AddMvc();
// for seeding the database with the demo user details
//services.AddTransient<IDatabaseInitializer, DatabaseInitializer>();
services.AddScoped<OpenIddictManager<ApplicationUser, Application>, CustomOpenIddictManager>();
}
Not sure what to do about this as I can't add a DbContext when using AddIdentity.
My connection string is fine, everything was working before adding OpenIddict.
UPDATE
Here is my appsettings.json file:
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Verbose",
"System": "Information",
"Microsoft": "Information"
}
},
"Data": {
"DefaultConnection": {
"ConnectionString": "my connection string"
},
"SaleboatContext": {
"ConnectionString": "my connection string"
}
}
}
My DbContext:
public class ApplicationUser : IdentityUser { }
public partial class MyDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
Due to a design change in EntityFramework Core RC2, you now need to flow the DbContextOptions manually or configure your connection string directly in OnModelCreating.
Try adding this constructor to your DB context (that should derive from OpenIddictContext):
public partial class MyDbContext : OpenIddictContext<ApplicationUser> {
public MyDbContext(DbContextOptions options)
: base(options) {
}
}