Database.Migrate method - asp.net

i want to apply migrations automatically for my app
so i used this method
Database.Migrate()
Applies any pending migrations for the context to the database. Will create the database if it does not already exist.
so as i understood when i run my application it should do as follow
1- create database if it doesn't exist
2- update database => apply migration to database
but when I run my project nothing happens
i write command manually to apply database and it's work but i don't know where is the problem
this is my code
dbContext
using Microsoft.Extensions.Configuration;
using Microsoft.EntityFrameworkCore;
using brokerTest.Entites;
namespace brokerTest.Brokers.Sotrage
{
public class StorageBroker : DbContext , IStorageBroker
{
private readonly IConfiguration configuration;
public StorageBroker(IConfiguration configuration)
{
this.configuration = configuration;
this.Database.Migrate();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){
var connectionString = this.configuration.GetConnectionString("DefaultConnection");
optionsBuilder.UseSqlServer(connectionString);
}
public DbSet<User> Users { get; set; }
}
}
startup.cs
services.AddDbContext<StorageBroker>();
services.AddScoped<IStorageBroker, StorageBroker>();
appsettingfile.json
{
"ConnectionStrings": {
"DefaultConnection": "server=(localdb)\\MSSQLLocalDB;Database=testDb"
},
Package installed
.NET 6 , Microsoft.EntityFrameworkCore.Design 5.0.8
Microsoft.EntityFrameworkCore.SqlServer 5.0.8

i found that class dbContext was not called at all because I didn't inject it with any controller or class
after i inject dbContext to my controller class
[ApiController]
[Route("/home")]
public class HomeController : Controller
{
private readonly IStorageBroker _storageBrodker;
public HomeController(IStorageBroker storageBroker)
{
_storageBrodker = storageBroker;
}
when i call route /home class is called then migrations apply
i thought it's called because i register it in services
but i didn't know it's called when need to resolve
and the comment for Sasan provide another way to do that from main when build iHost object
https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying?tabs=dotnet-core-cli#apply-migrations-at-runtime

Related

There was an error running the selected code generator: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContextOption

I am using Visual Studio 2022 Preview and .NET 6 SDK.
Here I am creating a webAPI project with 2 layers.
api project (Bgvsystem.webAPI)
class library (BgvSystem.Persistance)
NuGet packages-
Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version
6.0.0-rc.1.21452.10
Install-Package Microsoft.EntityFrameworkCore.Tools -Version
6.0.0-rc.1.21452.10
Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design
-Version 6.0.0-rc.1.21464.1
When I try to add a controller using scaffolding, I get the below error
There was an error running the selected code generator: unable to resolve service for type 'microsoft.entityframeworkcore.dbcontextoption.. While attempting to activate Dbcontext in .net 6 and visual studio 2022 preview
How to resolve this? Please help with this.
You can create a dbcontext factory class in same folder with your ApplicationDbContext class.
This factory class creates ApplicationDbContext at design time and scaffolding runs correctly.
Source: https://github.com/dotnet/Scaffolding/issues/1765
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer("Server=(localdb)\\MSSQLLocalDB;Database=EcommerceDb;Trusted_Connection=True;MultipleActiveResultSets=true");
return new ApplicationDbContext(optionsBuilder.Options);
}
}
After struggling for 3 days, finally I found the mistake and fixed that.
Actually I had to put connection string in MyApp.Persistance.DbContextClass as below
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Data Source=DESKTOP-SV8GPJ2\\SQLEXPRESS;Initial Catalog=StarterAppDB;Persist Security Info=True;User ID=sa;Password=Admin#1234");
}
}
Then it worked fine.
You can create a dbcontext factory class in same folder with your ApplicationDbContext class.
This factory class creates ApplicationDbContext at design time and scaffolding runs correctly.
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsBuilder.UseSqlServer("Server=YourServer; Database=YourDb; Integrated Security=true; MultipleActiveResultSets=true; Trusted_Connection=True");
return new ApplicationDbContext(optionsBuilder.Options);
}
}
In case of .NET 6
Can you try like this
Program.cs
builder.Services.AddDbContext<YourDbContext>(options =>
options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));
appsettings.json
"ConnectionStrings": {
"DefaultConnection": "Server=YourServer; Database=YourDb; Integrated Security=true; MultipleActiveResultSets=true; Trusted_Connection=True"
}
YourDbContext.cs
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Server=YourServer; Database=YourDb; Integrated Security=true; MultipleActiveResultSets=true; Trusted_Connection=True");
}
}
You need to add the DBContext to service collection in startup.cs file or pass as constructor BGvSystemContext class.
If you don't need the options builder for additional configuration you can dependency inject IConfiguration and use that to pass the connection string inside of OnConfiguring. This will solve the problem with ef code generation.
public class ApplicationDbContext : DbContext
{
private IConfiguration _config;
public ApplicationDbContext(IConfiguration config)
{
_config = config;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_config.GetConnectionString("DefaultConnection"));
}
}
And in startup you can use the paramaterless constructor for your db context instead of options builder
I am using N-Tier Architecture and asp net Area, so along with your ApplicationDbContext class add another class (DataContextFactory) class with following code
Good luck!
public class DataContextFactory : IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json")
.Build();
var optionsBuilder = new DbContextOptionsBuilder<AppDbContext>();
optionsBuilder.UseSqlServer(configuration.GetConnectionString("KasicaConnection"));
return new AppDbContext(optionsBuilder.Options);
}

asp.net core web api, how to inject the connection string

Here is my asp.net core project structure
1- ASP.NET CORE Web API (contains aspsettings.json)
"ConnectionStrings": {
"DefaultConnection": "Server=(local)\\SQLEXPRESS;Database=testdb;Trusted_Connection=True;"
}
2-SERVICES Project (Web API Call method from Services Project)
3-REPOSITORY Project (Services call method from Repository Project and Repository Project include the DATA Project where all the models are)
4-DATA Project where it's contain all the model with code first
public class TtEntities : DbContext
{
public virtual DbSet<RoomMessage> RoomMessage { get; set; }
public virtual DbSet<UserRoom> UserRoom { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=(local)\SQLEXPRESS;Database=testdb;Trusted_Connection=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
....
As you can see, I hardcoded the connection on the method OnConfiguring which is not the best practice for sure.
Is there a way to pass the connection string from the configuration file of the Web API Project?
Is update database command will still work if we pass the connection from the file aspsettings.json from web api project ?
Thanks a lot
A simple solution is like this:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
}
Consider how DefaultConnection is used in line 13. Also a sample appsettings is like as follow:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-WebApplication5;"
}
}
DI solves this problem perfectly and .NET Core 2.0 has Microsoft DI thats provides clearly experience with DI.
oh, lets starts(i think that DATA Project and REPOSITORY Project should be one)
from REPOSITORY Project
change your REPOSITORYClass to
public class REPOSITORYClass
{
private readonly TtEntities _db;
public REPOSITORYClass (TtEntities db){
_db = db;
}
//some your staff of REPOSITORYClass thats uses _db
}
now go to SERVICES Project
lets change some service that uses REPOSITORYClass
public class SomeService
{
private readonly REPOSITORYClass _repo;
public SomeService (REPOSITORYClass repo){
_repo = repo;
}
//other staff of SomeService thats uses _repo
}
after that go to ASP.NET CORE Web API startup file and add to
public void ConfigureServices
// Get connection of your repo
string connection = Configuration.GetConnectionString("DefaultConnection");
// add TtEntities as service
services.AddDbContext<TtEntities>(options =>
options.UseSqlServer(connection));
//add your repo
services.AddTransient<REPOSITORYClass>();
//add your service
services.AddTransient<SomeService>();
now go to the contoller thats uses your SomeService
public class SomeController: Controller
{
private readonly SomeService _someService;
public SomeController(SomeService someService){
_someService = someService;
}
//And use whatever your wants from your service that injected with deps of repo and injected db entity with connection
public string SomeMethod()
{
return _someService.SomeMethod();
}
}
And use whatever your wants from your service that injected with deps of repo and injected db entity with connection
thats all
PS also recommend to read this Introduction to Dependency Injection in ASP.NET Core

Migrations Seed Method Ignores Seed Method of Database Initializer

I'm working on an Asp.Net Mvc application then i want to seed my database using custom Database initializer class that I created.
so here is my DbContext :
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> {
public ApplicationDbContext()
: base("defaultConnection", throwIfV1Schema: false) {
}
static ApplicationDbContext() {
Database.SetInitializer(new ApplicationDbInitializer());
}
and here is my ApplicationDbInitializer class :
public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext> {
protected override void Seed(ApplicationDbContext context) {
InitializeIdentityForEF(context);
base.Seed(context);
}
public static void InitializeIdentityForEF(ApplicationDbContext db) {
// Create Role Admin if it does not exist
// Add user admin to Role Admin if not already added
// And some more initializing stuff
}
}
Note: I'm using Migrations too.
so when i try to update and seed my database using command Update-Database (even if I delete database manually) , the database is being created but it does not seed it.
if I am doing anything wrong or it has other ways I'll be thankful to get your assistance.
This sounds like the same issue as in this question.
So, the seed code will only run when you create an instance of your ApplicationDbContext.
Add some code like this to your application startup:
var ctx = new ApplicationDbContext();
ctx.Database.Initialize(true);
Then your database will be seeded the next time your app starts.
Alternatively, if you wanted to use the Update-Database command to seed your database, that uses a different Seed method. You can see an example here.

ASP.NET 5 - Using a Configuration Setting

I am playing with ASP.NET 5. I am trying to understand the new configuration model. I have read several articles. However, I am still unsuccessful in loading a configuration setting. My config.json file looks like this:
{
"App" : {
"Info" : {
"Version":"1.0.0",
"ReleaseDate":"03-15-2015"
}
}
}
My Startup.cs file looks like this:
public class Startup
{
public IConfiguration Configuration { get; private set; }
public Startup()
{
Configuration = new Configuration()
.AddJsonFile("config.json");
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseErrorPage();
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index" });
});
app.UseMvc();
app.UseWelcomePage();
}
}
In one of my controllers, I have the following
MyController.cs
using System;
using Microsoft.AspNet.Mvc;
namespace MyOrg.MyApp
{
public class MyController : Controller
{
[HttpGet()]
public ActionResult Index()
{
var version = Configuration.Get("App:Info:Version");
return new HttpStatusCodeResult(200);
}
}
}
When I start the app, I get an error that says:
error CS0103: The name 'Configuration' does not exist in the current context
at Microsoft.Framework.Runtime.Roslyn.RoslynProjectReference.Load(IAssemblyLo
adContext loadContext)
at Microsoft.Framework.Runtime.Loader.ProjectAssemblyLoader.Load(String name,
IAssemblyLoadContext loadContext)
at Microsoft.Framework.Runtime.Loader.ProjectAssemblyLoader.Load(String name)
at kre.host.LoaderContainer.Load(String name)
at kre.hosting.RuntimeBootstrapper.<>c__DisplayClass6_0.<ExecuteAsync>b__4(As
semblyName assemblyName)
at kre.hosting.RuntimeBootstrapper.<>c__DisplayClass6_0.<ExecuteAsync>b__7(Ob
ject sender, ResolveEventArgs a)
at System.AppDomain.OnAssemblyResolveEvent(RuntimeAssembly assembly, String assemblyFullName)
What am I doing wrong? I feel like I've followed the examples I've seen. Yet, I can figure out what I'm doing wrong.
Clearly you want to access Configuration property in your Startup class. And the error method says it doesn't know what Configuration is. So you need a using statement or a fully qualified name. Also, you should avoid naming things the same thing as stuff found in the framework. Your Startup class has a Configuration property, but it also tries to use the Configuration class from Microsoft.Framework.ConfigurationModel. How confusing is that?
Your Configure() method in Startup needs a using statement or fully qualified name so it knows what the Configuration class is.
using Microsoft.Framework.ConfigurationModel; //at the top of your class
Configuration = new Configuration(); //later in the code, we can access without fully qualifying name
or
Configuration = new Microsoft.Framework.ConfigurationModel.Configuration();
In your controller, you may have a similar issue. Replace MyOrg.MyApp.Startup in the example below with whatever the namespace is for your Startup class.
using MyOrg.MyApp.Startup //at the top of your class
Startup.Configuration.Get("App:Info:Version"); //later in the code, we can access without fully qualifying name
or
MyOrg.MyApp.Startup.Startup.Configuration.Get("App:Info:Version");
Better way of doing things
That should be enough to get you started. However, accessing the Startup class to retrieve your configuration isn't ideal, because now your controller's action methods depend on having the Startup class there. That's not very unit testable. Ideally your controllers should be isolated from each other. You should define some sort of interface to hold the configuration info you want, then have the controller depend on that interface. When you're in your site, you'll respond with a class specific to the site's configuration. When unit testing, you can have tight control over the test values by using a different class.
interface ISiteConfig
{
string Version {get; set;}
DateTime ReleaseDate {get; set;}
}
public class SiteConfig : ISiteConfig
{
public string Version {get; set;}
public DateTime ReleaseDate {get; set;}
public SiteConfig()
{
var c = new Configuration()
.AddJsonFile("config.json");
Version = c.Get("App:Info:Version");
ReleaseDate = c.Get("App:Info:ReleaseDate"); //may need to parse here
}
}
public class TestConfig : ISiteConfig
{
public string Version {get; set;}
public DateTime ReleaseDate {get; set;}
public TestConfig(string version, DateTime releaseDate)
{
Version = version;
ReleaseDate = releaseDate;
}
}
Then you'd use Dependency Injection to inject instances of your configuration into the Controller.
public class MyController : Controller
{
private readonly ISiteConfig Config;
public MyController(ISiteConfig config)
{
Config = config;
}
[HttpGet()]
public HttpStatusCodeResult Index()
{
var version = Config.Version;
return new HttpStatusCodeResult(200);
}
}
public class Startup
{
public void Configure(IBuilder app)
{
...
app.UseServices(services =>
{
...
// Set up the dependencies
services.AddTransient<ISiteConfig, SiteConfig>();
...
});
...
}
}
Now you can more easily unit test your action methods, because your unit tests can use the TestConfig class while the site can use the SiteConfig class. And also if you want to change how your configuration is done, you don't have to replace strings in a bunch of different places. You'll have one class where you do so, the rest is strongly typed and easy to change without blowing up your application.
Your unit test might look like this:
//Arrange
var testConfig = new TestConfig("1.0", DateTime.Now );
var controller = new MyController(testConfig );
//Act
var response = controller.Index();
//Assert
Assert.AreEqual(200, response.StatusCode);
As of Beta 5 the accepted answer is no longer correct. There is no longer a Get method on IConfiguration. Also the way of constructing the configuration object is also changed.
The following code works on Beta 7:
// showing using statements here since this is new from Beta 5
using Microsoft.Dnx.Runtime; // renamed was Microsoft.Framework.Runtime
using Microsoft.Framework.Configuration; // renamed was Microsoft.Framework.ConfigurationModel
// other using statements here
// Startup constructor. Note: now takes IApplicationEnvironment
// this is required in order to get base path
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
// Setup configuration sources.
var builder = new ConfigurationBuilder(appEnv.ApplicationBasePath)
.AddJsonFile("config.json")
.AddJsonFile("dbconfig.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
}
// property to hold configuration object created in constructor
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
// this will bind to an IOptions<AppSettings> instance
// where AppSettings is a class you define that has a set of
// properties that match your configuration section loaded from the
// json file
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
// here I am loading a connection string from a json file and passing into an
// new EF 6.x DB Context class
services.AddInstance<TalentAgencyContainer>(new TalentAgencyContainer(Configuration["ConnectionStrings:TalentAgencyContainer"]));
// Add MVC services to the services container.
services.AddMvc();
}

How can you inject an asp.net (mvc2) custom membership provider using Ninject?

OK, so I've been working on this for hours. I've found a couple of posts here, but nothing that actually resolves the problem. So, let me try it again...
I have an MVC2 app using Ninject and a custom membership provider.
If I try and inject the provider using the ctor, I get an error: 'No parameterless constructor defined for this object.'
public class MyMembershipProvider : MembershipProvider
{
IMyRepository _repository;
public MyMembershipProvider(IMyRepository repository)
{
_repository = repository;
}
I've also been playing around with factories and Initialize(), but everything is coming up blanks.
Any thoughts/examples?
The Membership provider model can only instantiate a configured provider when it has a default constructor. You might try this using the Service Locator pattern, instead of using Dependency Injection. Example:
public class MyMembershipProvider : MembershipProvider
{
IMyRepository _repository;
public MyMembershipProvider()
{
// This example uses the Common Service Locator as IoC facade, but
// you can change this to call NInject directly if you wish.
_repository = ServiceLocator.Current.GetInstance<IMyRepository>;
}
This is how I was able to do this:
1) I created a static helper class for Ninject
public static class NinjectHelper
{
public static readonly IKernel Kernel = new StandardKernel(new FooServices());
private class FooServices : NinjectModule
{
public override void Load()
{
Bind<IFooRepository>()
.To<EntityFooRepository>()
.WithConstructorArgument("connectionString",
ConfigurationManager.ConnectionStrings["FooDb"].ConnectionString);
}
}
}
2) Here is my Membership override:
public class FooMembershipProvider : MembershipProvider
{
private IFooRepository _FooRepository;
public FooMembershipProvider()
{
NinjectHelper.Kernel.Inject(this);
}
[Inject]
public IFooRepository Repository
{
set
{
_FooRepository = value;
}
}
...
With this approach it doesn't really matter when the Membership provider is instantiated.
I had the same problem at the exact same spot in the book. It wasn't until later on in the book that I noticed there were two separate web.config files. I initially placed my connectionString key in the wrong web.config file. It wasn't until I placed the connectionString in the correct web.config file that the 'no parameterless constructor' error went away.

Resources