NHibernate don't persists with session.Save and transaction.Commit - c#

I'm trying to configure NHibernate on .net core but still no sucess.
I can read the data, but when I try to save or delete, it doesn't work.
There is too much information like how I created my services, repository and mapping, so I will skip some files in this question, but everything is avaliable at my git repo.
So I have a very simple model.
public class Book
{
public virtual Guid Id { get; set; }
public virtual string Title { get; set; }
}
I also created a extension method for adding nhibernate in my service
public static class NHibernateExtensions
{
public static IServiceCollection AddNHibernate(this IServiceCollection services, string connectionString)
{
var mapper = new ModelMapper();
mapper.AddMappings(typeof(NHibernateExtensions).Assembly.ExportedTypes);
HbmMapping domainMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
var configuration = new Configuration()
.DataBaseIntegration(c =>
{
c.Dialect<MsSql2012Dialect>();
c.ConnectionString = connectionString;
c.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
c.SchemaAction = SchemaAutoAction.Validate;
c.LogFormattedSql = true;
c.LogSqlInConsole = true;
});
configuration.AddMapping(domainMapping);
var fluentSessionFactory = Fluently
.Configure(configuration)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Book>())
.BuildSessionFactory();
var sessionFactory = configuration.BuildSessionFactory();
services.AddSingleton(fluentSessionFactory);
services.AddScoped(factory => fluentSessionFactory.OpenSession());
services.AddScoped<ISessionManager, SessionManager>();
return services;
}
}
Here is my StartUp
public void ConfigureServices(IServiceCollection services)
{
var connStr = Configuration.GetConnectionString("DefaultConnection");
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
services.AddNHibernate(connStr);
services.AddTransient<IBookRepository, BookRepository>();
services.AddTransient<IBookService, BookService>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
And I created a BaseRepository for handle simple repository actions.
The problem I'm having is that in BaseRepository, when I call Add, it doesn't persists in the database.
public void Delete(T entity){
using (var transaction = Session.BeginTransaction())
{
Session.Delete(entity);
transaction.Commit();
Session.Flush();
}
}
When I call Queryable.ToList(), I get everything as expected.
What I'm doing wrong on the configurations that it doesn't persists in the db?
Observation: The database is SQL Server 2017 and is running on a docker container.

That's because you open new session on each Session access:
protected ISession Session => SessionFactory.OpenSession();
Transaction is started in one session add/delete in other flush in third. Obviously you need to do all operations in one session.
Also you don't need to call Flush by default - it should be called automatically on transaction.Commit. And if you really need to call Flush - do it before transaction commit.

Related

How to access Singleton directly from ConfigureServices without BuildServiceProvider?

How to access singletons from ConfigureServices? There's a reason that I can't use appsettings for few configs.
For example, let's say that I want to set swagger title and version from database, not appsettings. My actual problem is I want to set consul address from my database. The problem should be the same, that I need to access my database in ConfigureServices. I have a custom extension like this:
public static IServiceCollection AddConsulConfig(this IServiceCollection services, string address)
{
services.AddSingleton<IConsulClient, ConsulClient>(p => new ConsulClient(consulConfig =>
{
consulConfig.Address = new Uri(address);
}));
return services;
}
I call it from startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IGlobalParameter, GlobalParameterManager>();
//I want to use IGlobalParameter here directly but without BuildServiceProvider
//This part is the problem
var service = ??
var varTitle = service.GetById("Title").Result.Value;
var varConsulAddress = service.GetById("ConsulAddress").Result.Value;
services.AddConsulConfig(varConsulAddress);
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = varTitle, Version = "v1" });
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// I can use it here or in the controller no problem
var service = app.ApplicationServices.GetRequiredService<IGlobalParameter>();
var varTitle = service.GetById("Title").Result.Value;
var varConsulAddress = service.GetById("ConsulAddress").Result.Value;
}
I DO NOT want to use BuildServiceProvider as it will make multiple instances, even visual studio gives warning about it. referenced in How to Resolve Instance Inside ConfigureServices in ASP.NET Core
I knew the existence of IConfigureOptions from the following link
https://andrewlock.net/access-services-inside-options-and-startup-using-configureoptions/#the-new-improved-answer
But, I can't seem to find how exactly do I use that in ConfigureService:
public class ConsulOptions : IConfigureOptions<IServiceCollection>
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ConsulOptions(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Configure(IServiceCollection services)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var provider = scope.ServiceProvider;
IGlobalParameter globalParameter = provider.GetRequiredService<IGlobalParameter>();
var ConsulAddress = globalParameter.GetById("ConsulAddress").Result.Value;
services.AddConsulConfig(ConsulAddress);
}
}
}
Set it in startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IGlobalParameter, GlobalParameterManager>();
services.AddSingleton<IConfigureOptions<IServiceCollection>, ConsulOptions>(); // So what? it's not called
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// IConsulClient is still null here
}
Any solution to how do I achieve this?
Thank you Jeremy, it's as simple as that. I don't know why I spend way too much time figuring out how to set this
The solution is to add singleton :
services.AddSingleton<IConsulClient, ConsulClient>(
p => new ConsulClient(consulConfig =>
{
var ConsulAddress = p.GetRequiredService<IGlobalParameter>().GetById("ConsulAddress").Result.Value;
consulConfig.Address = new Uri(ConsulAddress);
}
));

Inject a service in Startup.cs in ASP.NET Core 3.1

I am working on a .NET Core 3.1 application. I have a requirement where i have to inject a service in Startup.cs. My code is:
Program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices(servicesCollection =>
{
servicesCollection.AddScoped<IUnauthorizedAccessService, UnauthorizedAccessService>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs:
public Startup(IConfiguration configuration, IUnauthorizedAccessService unauthorizedAccessService)
{
Configuration = configuration;
_unauthorizedAccessService = unauthorizedAccessService;
}
public IConfiguration Configuration { get; }
public IUnauthorizedAccessService _unauthorizedAccessService { get; set; }
When i run the code, i get the following exception:
Unable to resolve service for type 'Interface.Service.IUnauthorizedAccessService' while attempting to activate 'Website.Startup'.'
How can i inject the service in Startup.cs ? I have even tried it getting in Configure method. But then, i get the exception at repository level. Code:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IUnauthorizedAccessService unauthorizedAccessService)
{
_unauthorizedAccessService = unauthorizedAccessService;
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.UseSession();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCookiePolicy(new CookiePolicyOptions
{
MinimumSameSitePolicy = SameSiteMode.Strict,
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=User}/{action=Index}/{id?}");
});
}
I have a method RegisterDatabase which is being called from ConfigureServices. Code:
private void RegisterDatabase(IServiceCollection services)
{
services.AddDbContext<TrainingContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
Service code is:
public class UnauthorizedAccessService : IUnauthorizedAccessService
{
private readonly IEventLogRepository _eventLogRepository;
public UnauthorizedAccessService(IEventLogRepository eventLogRepository)
{
_eventLogRepository = eventLogRepository;
}
public async Task<BaseResponse> LogUnauthorizedAccessInDB(string user, string url, string sessionId)
{
try
{
EventLog eventLog = new EventLog();
eventLog.Httpsession = sessionId;
eventLog.AppUserName = user;
eventLog.EventDateTime = DateTime.Now;
eventLog.MessageLevel = 3;
eventLog.Message = url;
await _eventLogRepository.Add(eventLog);
}
catch(Exception ex)
{
}
return HelperService.Response(null, null);
}
}
When Adding the object, i get the exception
Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'TrainingContext'.
All of my other repositories are working but, getting exception only at this point. What can be the possible issue ? Any help would be much appreciated.
Basically, what i am trying to achieve is that i want to log unauthorized access to my site in Database. Code is:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(o =>
{
o.AccessDeniedPath = "/Home/Error";
o.LoginPath = "/Login";
o.SlidingExpiration = false;
o.Events = new CookieAuthenticationEvents
{
//OnRedirectToAccessDenied = new Func<RedirectContext<CookieAuthenticationOptions>, Task>(context =>
OnRedirectToAccessDenied = new Func<RedirectContext<CookieAuthenticationOptions>, Task>(test)
};
});
test method is:
private async Task<Task> test (RedirectContext<CookieAuthenticationOptions> context)
{
string user = context.HttpContext.User.Identity.Name;
string url = "/" + context.Request.Host.Value + "/" + context.Request.RouteValues["controller"] + "/" + context.Request.RouteValues["action"];
string sessionId = context.HttpContext.Session.Id;
await _unauthorizedAccessService.LogUnauthorizedAccessInDB(user, url, sessionId);
context.Response.Redirect("/Home/Error");
return context.Response.CompleteAsync();
}
You need to create a scoped object that implements CookieAuthenticationEvents. For example:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using System.Threading.Tasks;
namespace MyApplication.Services
{
public class MyCookieAuthenticationEvents : CookieAuthenticationEvents
{
private readonly IUnauthorizedAccessService _unauthorizedAccessService;
public MyCookieAuthenticationEvents(
IUnauthorizedAccessService unauthorizedAccessService)
{
_unauthorizedAccessService = unauthorizedAccessService;
}
public override Task RedirectToAccessDenied(
RedirectContext<CookieAuthenticationOptions> context)
{
// TODO: you can use _unauthorizedAccessService here
return base.RedirectToAccessDenied(context);
}
}
}
To inject this, you'd do it as so:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.EventsType = typeof(MyCookieAuthenticationEvents);
});
services.AddScoped<MyCookieAuthenticationEvents>();
services.AddScoped<IUnauthorizedAccessService, UnauthorizedAccessService>();
Make sure you remove that IUnauthorizedAccessService from your program.cs. You don't inject there. You inject in your Configure method.
This is how you do proper dependency injection. You don't do what the accepted answer is doing. That is probably one of the most unorthodox things I have ever seen in a long time.
Startup.cs is designed for configuring own services and pipeline configuration. You can not inject your custom services in constructor just because they are not configured yet.
Docs:
The host provides services that are available to the Startup class
constructor. The app adds additional services via ConfigureServices.
Both the host and app services are available in Configure and
throughout the app.

Overriding database provider in integration test with WebApplicationFactory

I am following the official MS documentation for integration testing .Net Core (https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.1).
I was able to get the first part of the integration test done where I was not overriding the startup class of the application I am testing (i.e. I was using a web application factorythat did not override any services).
I want to override the database setup to use an in-memory database for the integration test. The problem I am running into is that the configuration continues to try and use the sql server for services.AddHangfire().
How do I override only above specific item in my integration test? I only want to override the AddHangfire setup and not services.AddScoped<ISendEmail, SendEmail>(). Any help would be appreciated.
Test Class with the custom web application factory
public class HomeControllerShouldCustomFactory : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Startup> _factory;
public HomeControllerShouldCustomFactory(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async Task IndexRendersCorrectTitle()
{
var response = await _client.GetAsync("/Home/Index");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("Send Email", responseString);
}
}
Custom Web Application Factory
public class CustomWebApplicationFactory<TStartup>: WebApplicationFactory<SendGridExample.Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
var inMemory = GlobalConfiguration.Configuration.UseMemoryStorage();
services.AddHangfire(x => x.UseStorage(inMemory));
// Build the service provider.
var sp = services.BuildServiceProvider();
});
}
}
My startup.cs in my application that I am testing
public IConfiguration Configuration { get; }
public IHostingEnvironment Environment { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("ASP_NetPractice")));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddScoped<ISendEmail, SendEmail>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseHangfireServer();
app.UseHangfireDashboard();
RecurringJob.AddOrUpdate<ISendEmail>((email) => email.SendReminder(), Cron.Daily);
app.UseMvc();
Update
I don't see this issue in my other example project where I am using only entity framework. I have a simple application with an application db context which uses SQL server. In my test class, I override it with an in-memory database and everything works. I am at a loss at to why it will work in my example application but not work in my main application. Is this something to do with how HangFire works?
In my test application (example code below), I can delete my sql database, run my test, and the test passes because the application DB context does not go looking for the sql server instance but uses the in-memory database. In my application, the HangFire service keeps trying to use the sql server database (if I delete the database and try to use an in-memory database for the test - it fails because it can't find the instance its trying to connect to). How come there is such a drastic difference in how the two projects work when a similar path is used for both?
I ran through the debugger for my integration test which calls the index method on the home controller above (using the CustomWebApplicationFactory). As I am initializing a test server, it goes through my startup class which calls below in ConfigureServices:
services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("ASP_NetPractice")));
After that, the Configure method tries to call below statement:
app.UseHangfireServer();
At this point the test fails as It cannot find the DB. The DB is hosted on Azure so I am trying to replace it with an in-memory server for some of the integration test. Is the approach I am taking incorrect?
My example application where its working
Application DB Context in my example application
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public virtual DbSet<Message> Messages { get; set; }
public async Task<List<Message>> GetMessagesAsync()
{
return await Messages
.OrderBy(message => message.Text)
.AsNoTracking()
.ToListAsync();
}
public void Initialize()
{
Messages.AddRange(GetSeedingMessages());
SaveChanges();
}
public static List<Message> GetSeedingMessages()
{
return new List<Message>()
{
new Message(){ Text = "You're standing on my scarf." },
new Message(){ Text = "Would you like a jelly baby?" },
new Message(){ Text = "To the rational mind, nothing is inexplicable; only unexplained." }
};
}
}
Startup.cs in my example application
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
CustomWebApplicationFactory - in my unit test project
public class CustomWebApplicationFactory<TStartup>
: WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Add a database context (ApplicationDbContext) using an in-memory
// database for testing.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
options.UseInternalServiceProvider(serviceProvider);
});
// Build the service provider.
var sp = services.BuildServiceProvider();
});
}
}
My unit test in my unit test project
public class UnitTest1 : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<Startup> _factory;
public UnitTest1(CustomWebApplicationFactory<Startup> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async System.Threading.Tasks.Task Test1Async()
{
var response = await _client.GetAsync("/");
//response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
Assert.Contains("Home", responseString);
}
Update 2
I think I found an alternate to trying to override all my configuration in my integration test class. Since it's a lot more complicated to override HangFire as opposed to an ApplicationDBContext, I came up with below approach:
Startup.cs
if (Environment.IsDevelopment())
{
var inMemory = GlobalConfiguration.Configuration.UseMemoryStorage();
services.AddHangfire(x => x.UseStorage(inMemory));
}
else
{
services.AddHangfire(x => x.UseSqlServerStorage(Configuration["DBConnection"]));
}
Then in my CustomWebApplicationBuilder, I override the environment type for testing:
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<SendGridExample.Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Development"); //change to Production for alternate test
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
});
}
}
With that approach, I don't need to worry about having to do extra logic to satisfy hangfire's check for an active DB. It works but I am not 100% convinced its the best approach as I'm introducing branching in my production startup class.
There are two different scenarios you need to check.
Create a job by class BackgroundJob
Create a job by interface IBackgroundJobClient
For the first option, you could not replace the SqlServerStorage with MemoryStorage.
For UseSqlServerStorage, it will reset JobStorage by SqlServerStorage.
public static IGlobalConfiguration<SqlServerStorage> UseSqlServerStorage(
[NotNull] this IGlobalConfiguration configuration,
[NotNull] string nameOrConnectionString)
{
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
if (nameOrConnectionString == null) throw new ArgumentNullException(nameof(nameOrConnectionString));
var storage = new SqlServerStorage(nameOrConnectionString);
return configuration.UseStorage(storage);
}
UseStorage
public static class GlobalConfigurationExtensions
{
public static IGlobalConfiguration<TStorage> UseStorage<TStorage>(
[NotNull] this IGlobalConfiguration configuration,
[NotNull] TStorage storage)
where TStorage : JobStorage
{
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
if (storage == null) throw new ArgumentNullException(nameof(storage));
return configuration.Use(storage, x => JobStorage.Current = x);
}
Which means, no matter what you set in CustomWebApplicationFactory, UseSqlServerStorage will reset BackgroundJob with SqlServerStorage.
For second option, it could replace IBackgroundJobClient with MemoryStorage by
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.AddSingleton<JobStorage>(x =>
{
return GlobalConfiguration.Configuration.UseMemoryStorage();
});
});
}
}
In conclusion, I suggest you register IBackgroundJobClient and try the second option to achieve your requirement.
Update1
For DB is not available, it could not be resolved by configuring the Dependency Injection. This error is caused by calling services.AddHangfire(x => x.UseSqlServerStorage(Configuration.GetConnectionString("ASP_NetPractice")));.
For resolving this error, you need to overriding this code in Startup.cs.
Try steps below:
Change Startup to below:
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)
{
//Rest Code
ConfigureHangfire(services);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//Rest Code
app.UseHangfireServer();
RecurringJob.AddOrUpdate(() => Console.WriteLine("RecurringJob!"), Cron.Minutely);
}
protected virtual void ConfigureHangfire(IServiceCollection services)
{
services.AddHangfire(config =>
config.UseSqlServerStorage(Configuration.GetConnectionString("HangfireConnection"))
);
}
}
Create StartupTest in test project.
public class StartupTest : Startup
{
public StartupTest(IConfiguration configuration) :base(configuration)
{
}
protected override void ConfigureHangfire(IServiceCollection services)
{
services.AddHangfire(x => x.UseMemoryStorage());
}
}
CustomWebApplicationFactory
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint> where TEntryPoint: class
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
return WebHost.CreateDefaultBuilder(null)
.UseStartup<TEntryPoint>();
}
}
Test
public class HangfireStorageStartupTest : IClassFixture<CustomWebApplicationFactory<StartupTest>>
{
private readonly HttpClient _client;
private readonly CustomWebApplicationFactory<StartupTest> _factory;
public HangfireStorageStartupTest(CustomWebApplicationFactory<StartupTest> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
}

Code first approach does not create table

I am developing application in Core 2.0 and using identity to create tables. So when I run application the database automatically create. Later when I try to run migration command it does not create table.
//DAL
public class ApplicationDbContext:IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options):base(options)
{
}
public DbSet<tblContact> tblContacts { get; set; }
//protected override void OnModelCreating(ModelBuilder builder)
//{
// base.OnModelCreating(builder);
//}
}
//Required Table Class
public partial class tblContact
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContactId { get; set; }
public string PhoneNumber { get; set; }
}
Following is commands that I ran
add-migration 20180921
update-database -verbose
At the end of output in console it says
Error Number:2714,State:6,Class:16
There is already an object named 'AspNetRoles' in the database.
One more thing that when I drop database and run application then required tables create automatically without running any command.
What I am missing here?
Following is Start.cs file
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
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.AddMvc();
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
});
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(config =>
{
// Cookie settings
config.ExpireTimeSpan = TimeSpan.FromHours(2);
config.SlidingExpiration = true;
config.LoginPath = "/Account/Login";
config.LogoutPath = "/Account/LogOut";
config.AccessDeniedPath = "/Account/AccessDenied";
});
services.AddTransient<IAccountBAL, AccountBAL>();
services.AddSingleton<IConfiguration>(Configuration);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAccountBAL _iAccountBAL)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
SeedDatabase.Initialize(app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope().ServiceProvider);
_iAccountBAL.CreateDefaultRoles().Wait();
_iAccountBAL.CreateSuperAdmin().Wait();
}
}
public static void Initialize(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetRequiredService<ApplicationDbContext>();
var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
context.Database.EnsureCreated();
}
Sounds like your application applying migrations when it is running. Please check your Startup.cs has migration. You need to remove it if your application has it in order to run migration from package manager console.
private static void InitializeMigrations(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
MyDbContext dbContext = serviceScope.ServiceProvider.GetRequiredService<MyDbContext>();
dbContext.Database.Migrate();
}
}
From MSDN
Ensures that the database for the context exists. If it exists, no action is taken. If it does not exist then the database and all its schema are created. If the database exists, then no effort is made to ensure it is compatible with the model for this context.
Note that this API does not use migrations to create the database. In addition, the database that is created cannot be later updated using migrations. If you are targeting a relational database and using migrations, you can use the DbContext.Database.Migrate() method to ensure the database is created and all migrations are applied.

Cannot resolve DbContext in ASP.NET Core 2.0

First of all, I'm trying to seed my database with sample data. I have read that this is the way to do it (in Startup.Configure) (please, see ASP.NET Core RC2 Seed Database)
I'm using ASP.NET Core 2.0 with the default options.
As usual, I register my DbContext in ConfigureServices.
But after that, in the Startup.Configure method, when I try to resolve it using GetRequiredService, it throws with this message:
System.InvalidOperationException: 'Cannot resolve scoped service
'SGDTP.Infrastructure.Context.SGDTPContext' from root
provider.'
My Startup class like this:
public abstract class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SGDTPContext>(options => options.UseInMemoryDatabase("MyDatabase"))
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
SeedDatabase(app);
}
private static void SeedDatabase(IApplicationBuilder app)
{
using (var context = app.ApplicationServices.GetRequiredService<SGDTPContext>())
{
// Seed the Database
//...
}
}
}
What am I doing wrong?
Also, is this the best place to create seed data?
You're registering SGDTPContext as a scoped service and then attempting to access it outside of a scope. To create a scope inside your SeedDatabase method, use the following:
using (var serviceScope = app.ApplicationServices.CreateScope())
{
var context = serviceScope.ServiceProvider.GetService<SGDTPContext>();
// Seed the database.
}
Credit to #khellang for pointing out the CreateScope extension method in the comments and to #Tseng's comment and answer re how to implement seeding in EF Core 2.
Was getting this error while following the official ASP.Net MVC Core tutorial, in the section where you are supposed to add seeded data to your application. Long story short, adding these two lines
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
to the SeedData class solved it for me:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
namespace MvcMovie.Models
{
public static class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = new MvcMovieContext(
serviceProvider.GetRequiredService<DbContextOptions<MvcMovieContext>>()))
{
// Look for any movies.
if (context.Movie.Any())
{
return; // DB has been seeded
}
...
Can't tell you the WHY, but these were two of the options I got from following the Alt + Enter quick fix option.
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)
{
var key = Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value);
services.AddDbContext<DataContext>(x => x.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")).EnableSensitiveDataLogging());
services.AddMvc();
services.AddTransient<Seed>();
services.AddCors();
services.AddScoped<IAuthRepository, AuthRepository>();
services.AddScoped<IUserRepository, UserRepository>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(Options =>
{
Options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
}
);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env ,Seed seeder)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
context.Response.AddApplicationError(error.Error.Message);
await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
}
});
});
}
seeder.SeedUser();
app.UseCors(x=>x.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials());
app.UseMvc();
}
}
}

Categories