Scaffold-DbContext SQL Views ? ASP NET CORE 3.1 - c#

I need your help.
I read on the web that there has never been a proper way to import views from sql into our asp.net core project.
Do you know if in version 3.1 you can do ? If so, how?
For tables I use the "scaffold-DbContext" command.
Thank you very much!

Although you cannot use scaffold-DbContext for database view but you can still use SQL View in your .Net Core project.
Suppose you have the following SQL View
CREATE VIEW [dbo].[v_MyTableResult] AS
SELECT
SELECT Id, Name FROM MyTable
GO
Create a new Model class based on the result set of SQL View.
public class MyTableModel
{
public int Id { get; set; }
public string Name { get; set; }
}
In the database context class, introduce property for the Model.
public virtual DbSet<MyTableModel> MyTableResults { get; set; }
In OnModelCreating method, configure model properties
modelBuilder.Entity<MyTableModel>(entity =>
{
entity.HasNoKey();
entity.Property(e => e.Id)
.HasColumnName("Id");
entity.Property(e => e.Name)
.HasColumnName("Name");
});
Finally, in the method where you need results from the view, call FromSqlRaw on DbSet property
var myTableResults =
this.MyDbContext.MyTableResults.FromSqlRaw(
"SELECT * FROM dbo.v_MyTableResult").ToList();

It is possible to scaffold a view. Just use -Tables the way you would to scaffold a table, only use the name of your view. E.g., If the name of your view is ‘vw_inventory’, then run this command in the Package Manager Console (substituting your own information for "My..."):
PM> Scaffold-DbContext "Server=MyServer;Database=MyDatabase;user id=MyUserId;password=MyPassword" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Temp -Tables vw_inventory
This command will create a model file and context file in the Temp directory of your project. You can move the model file into your models directory (remember to change the namespace name). You can copy what you need from the context file and paste it into the appropriate existing context file in your project.
Note: If you want to use your view in an integration test using a local db, you'll need to create the view as part of your db setup. If you’re going to use the view in more than one test, make sure to add a check for existence of the view. In this case, since the SQL ‘Create View’ statement is required to be the only statement in the batch, you’ll need to run the create view as dynamic Sql within the existence check statement. Alternatively you could run separate ‘if exists drop view…’, then ‘create view’ statements, but if multiple tests are running concurrently, you don’t want the view to be dropped if another test is using it.
Example:
void setupDb() {
...
SomeDb.Command(db => db.Database.ExecuteSqlRaw(CreateInventoryView()));
...
}
public string CreateInventoryView() => #"
IF OBJECT_ID('[dbo].[vw_inventory]') IS NULL
BEGIN EXEC('CREATE VIEW [dbo].[vw_inventory] AS
SELECT ...')
END";
This is a helpful link. It describes adding the code sections by hand (as Nouman mentioned) instead of scaffolding them: https://learn.microsoft.com/en-us/ef/core/modeling/keyless-entity-types?tabs=fluent-api

It seems not supported yet. For a workaround, you could refer to
Is it possible to automatically map a DB view on Entity Framework Core version 2.1?

Yes, we can use scaffold-DbContext with view.
using scaffold-DbContext for database view (myview). I executed below scaffold-DbContext query.
Scaffold-DbContext "Data Source=my_server,1433;Initial Catalog=my_database;User Id=my_user;Password=my_password;" Microsoft.EntityFrameworkCore.SqlServer -o Entities -Context ContextName -t myview -f
Once you execute then you will get entity class and contextfile.
Code in entity class (myview.cs)
////// Fetching data
public partial class myview
{
public Guid property_1 { get; set; }
}
Code in dbcontect file (ContextName.cs)
public partial class ContextName : DbContext
{
public virtual DbSet<myview> myview { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<myview>(entity =>
{
entity.HasNoKey();
entity.ToView("myview", "ExtIntDWFS");
////// other property configuration.
}
}
}
Code in controller to fetch view data
////// Fetching data
public async Task<myview> GetView(Base #base)
{
myview obj = new myview();
using (ContextName context = new ContextName())
{
obj = await context.myview.Select(s => new myview
{
property_1 = s.property_1,
}).FirstOrDefaultAsync();
}
return obj;
}
Above code is working for me successfully.

I recently had a similar need (retrieve one ore more views and create the corresponding classes in a .NET project). It can be done with Scaffold-DbContect, for example:
Scaffold-DbContext "Server=<address>;
Database=<dbname>;
Trusted_Connection=True"
Microsoft.EntityFrameworkCore.SqlServer -OutputDir
ModelDirectory -t <table_name> -t <view_name>
It creates a folder called OutputDir with the corresponding classes for the indicated tables or views

My solution was similar to Jainith's solution, but the trick for me was to make sure the solution compiled successfully before I ran the Scaffold command. Using .net core 6 and EF 6
I then ran my command:
Scaffold-DbContext "Server=MyServer;Database=myDb;user=theUser;password=thePwd;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entities
-ContextDir . -Context MyContext -UseDatabaseNames -Force -NoPluralize -NoOnConfiguring -Tables vwMyView
The command ran successfully and created my class in the root folder. I then moved it into my Models folder and went from there. That is optional of course.
There is more info here as to why you need to make sure the solution compiles before you run the scaffold command.

Related

Intercepting SqlServerMigrationsSqlGenerator to prevent migration of certain tables?

I am trying to use EF Core 3.0 migrations with a hybrid of an existing tables and new tables built with code first. To prevent the scaffolding of the existing tables I would like to decorate the model class with an attribute (fluently or annotations) so that the migration code generation for those tables is skipped but the model is still built into the DbContext class.
The approach I'm taking is to add the following lines to OnConfiguring
optionsBuilder.ReplaceService<IMigrationsSqlGenerator, SkipMigrator>();
And then creating a SkipMigrator with the following code
public class SkipMigrator:SqlServerMigrationsSqlGenerator
{
public SkipMigrator(
MigrationsSqlGeneratorDependencies dependencies,
IMigrationsAnnotationProvider migrationsAnnotations)
: base(dependencies, migrationsAnnotations){}
protected override void Generate(
MigrationOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
if (operation.FindAnnotation("SkipMigrations")!=null)
{
Console.WriteLine("Skipping table:");
}
else
{
base.Generate(operation,model,builder);
}
}
}
I assumed that the Generate method was what triggered the creation of the migration code file but it never gets called. Id there a different place I should be intercepting the code generation?
If there a different/simpler way to tell migrations to skip tables yet still keep them in my DbContext?
Your means of trying to create your own IMigrationsSqlGenerator was correct, and I've used that approach before to alter the SQL that is generated as part of a migration.
services.AddDbContext<MyDbContext>(opt =>
{
opt.UseSqlServer();
opt.ReplaceService<IMigrationsSqlGenerator, SkipMigrator>();
});
However, as of EF Core 5.0 it's now much easier to exclude specific tables from migrations using the ExcludeFromMigrations() method on a TableBuilder:
public class ReportingContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable(nameof(Users), t => t.ExcludeFromMigrations());
}
}
More info: https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-efcore-5-0-rc1/#exclude-tables-from-migrations
If there a different/simpler way to tell migrations to skip tables yet still keep them in my DbContext?
Yes, but it requires a different approach.
Instead of using your current DbContext class for creating migrations, create a SecondDbContext class only for the very purpose of creating migrations. This new SecondDbContext class will hold the DbSets<T> that you want EF to do its migrations on.
Then simply specify the second context when calling add-migration UpdateTable -c SecondDbContext and then update-database -c SecondDbContext.

DropCreateDatabaseIfModelChanges when EF is another project

I have separated my solution in separate projects, a DAL project with entity framework and an ASP.NET MVC project.
I want to use DropCreateDatabaseIfModelChanges, but I don't know where to put it to make it work.
I've tried to put it in the web.config of the MVC project and the app.config of the DAL project (both by making use of the context element), I've tried putting it in the global.asax (Database.SetInitializer(new DropCreateDatabaseIfModelChanges<BreakAwayContext>());), I've tried a custom initialization class, but none of these seem to work.
If possible, I don't want to make use of migrations. How can I make it work?
You could create a class to implement CreateDatabaseIfNotExists and call Database.SetInitializer function in Application_Start().
-DbInitializer
public class MyDbInitializer : CreateDatabaseIfNotExists<MyDbContext>
{
protected override void Seed(MyDbContext context)
{
//Data initializing...
}
}
-Application_Start
protected void Application_Start()
{
Database.SetInitializer(new MyDbInitializer());
}
The database will be create when running the application.
And if you would like to do a automatic migration of database, use MigrateDatabaseToLatestVersion class
public class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
this.AutomaticMigrationsEnabled = true;
}
}
-Application_Start
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDbContext,Configuration>());
Howerver, I recomand that using migraion commands will be more flexible. See this walkthru: Overview of Entity Framework Code First Migrations with example, by Bhavik Patel.
I guess by 'database initialization' you actually mean 'updating the database schema'.
Set the EfRepository as start up project of the solution
Open the Package manager console Choose EfRepository as default project
Run the following commands:
Enable-Migrations -ConnectionStringName "EfDataRepository"
Add-Migration Initial -ConnectionStringName "EfDataRepository"
Update-Database -ConnectionStringName "EfDataRepository" -Script -SourceMigration:0
This will give you a .sql script. Execute it against your database (and usually store it as part of the solution - either Create.sql or some kind of a migration .sql, depends on whether you already have a schema or you are creating it from scratch).
Of course, replace EfDataRepository with the data connection name from your .config file.

Models.ApplicationDbContext for all models in an Asp.Net MVC 5 application?

I've creating an Asp.Net MVC 5 website. I will need to add customized fields in ApplicationUser and associate (add foreign keys) it with other models. I think I should just use one context type. However, the code scaffold already generate the following ApplicationDbContext class. Can I just put all my public DbSet<...> ... { get; set; } in the class? Or is there a better pattern?
namespace MyApp.Models
{
// You can add profile data for the user by adding more properties to your User class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : User
{
}
public class ApplicationDbContext : IdentityDbContextWithCustomUser<ApplicationUser>
{
}
}
There is an excellent video explaining that matter.
Just check the free ASP.NET MVC 5 Fundamentals course by Scott Allen.
The exact answer is here (starts at 3:30).
I would advise keeping them separate. There is really no reason to couple two parts of the system together. To add another DbContext just add a file to models called YourContext.cs.
public class YourContext: DbContext
{
public YourContext() : base("name=YourContext")
{
}
// Add a DbSet for each one of your Entities
public DbSet<Room> Rooms { get; set; }
public DbSet<Meal> Meals { get; set; }
}
Then in the root web.config
<add name="YourContext" connectionString="Data Source=(localdb)\v11.0; Initial Catalog=YourContext; Integrated Security=True"" providerName="System.Data.SqlClient" />
When you run enable-migrations in the package manager console you will be asked which dbcontext you want to migrate. Pick YourContext.
EDIT: No need to add repos / unit of work the Entity Framework does this for you.
Please note: This was written as in beta2 where ALLOT has changed! Hopefully most of it will stick but there are no guarantees until RC.
DO NOT USE NuGET package manger (until RC) as it does NOT pick-up on the .NET 5 packages required and it will install EF 6 and mess up your project. (We are after EF 7)
In the projects.json you need to have the following dependencies. (or beta2 when its out, or the latest on RC)
"EntityFramework": "7.0.0-beta1",
"EntityFramework.Relational": "7.0.0-beta1",
"EntityFramework.Commands": "7.0.0-beta1",
"EntityFramework.Migrations": "7.0.0-beta1",
"EntityFramework.SqlServer": "7.0.0-beta1"
Add a new folder DBContexts and add a c sharp file with your new context stuff.
public class BlaBlaDB : DbContext
{
public DbSet<Models.MyOtherModel> MyOtherModels { get; set; }
protected override void OnConfiguring(DbContextOptions options)
{
options.UseSqlServer();
}
}
and in your config.json make sure to add a connection string, the exact same as the IdentityDB just with you new name. Then in startup.json register your databse.
services.AddEntityFramework(Configuration)
.AddSqlServer()
.AddDbContext<DataContexts.IdentityDB>()
.AddDbContext<DataContexts.BlaBlaDB>();
This has to compile because k will run this project and use the startup to inject your context and then execute everything you need. As of now VS2015 Beta does NOT have all/ or they do not work, the command for EF.
You need to go and install KRE for Windows.
Open command prompt, browse to your project directory, enter the solution and enter the following commands.
k ef context list
k ef migration add -c (context.from.above) initial
k ef migration apply -c (context.from.above)
You now have multi context migration. Just keep on adding context and repeat this as you need it. I used this on localdb, as the default project set-up so that it can work stand alone in any environment, like Linux.
Please Note: You still need to create a Service, containing the Interface and Implementation and then register that in startup.json More information here

Can't make EF Code First work with manually changed data base

I've added two new properties to my domain model class and two properties to a data table accordingly. Then I tried to launch my mvc web application and got
The model backing the 'EFDbContext' context has changed since the database was created.
Consider using Code First Migrations to update the database
(http://go.microsoft.com/fwlink/?LinkId=238269).
Having read the following posts:
MVC3 and Code First Migrations
EF 4.3 Automatic Migrations Walkthrough
I tried to Update-Database through Package Manager Console, but got an error
Get-Package : Не удается найти параметр, соответствующий имени параметра "ProjectName".
C:\Work\MVC\packages\EntityFramework.5.0.0\tools\EntityFramework.psm1:611 знак:40
+ $package = Get-Package -ProjectName <<<< $project.FullName | ?{ $_.Id -eq 'EntityFramework' }
+ CategoryInfo : InvalidArgument: (:) [Get-Package], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,NuGet.PowerShell.Commands.GetPackageCommand
The EntityFramework package is not installed on project 'Domain'.
But the Entityframework is installed on project Domain. I removed it from references, deleted package.config and sucessfully reinstalled EF. But Update-Database still returns same error. Update-Database -Config does as well
What am I doing wrong?
EDIT
Many thanks to Ladislav Mrnka, I'll try to rephrase my question. As far as I changed my data table manually, I am not expected to use migration. But how can I now make EF work with manually edited domain model class and data table?
Try to add this to startup of your application (you can put it to App_Start):
Database.SetInitializer<EFDbContext>(null);
It should turn off all logic related to handling the database from EF. You will now be fully responsible for keeping your database in sync with your model.
I had the same problem and this is how I fixed the issue.
I dropped table __MigrationHistory using sql command and run the update-database -verbose again.
Apparently something was wrong with this automatic created table.
Answer 2 was exactly what was needed. Although when I got to the App_Start I realized that there were 4 configuration files and didn't see where this would fit in any of them. Instead I added it to my EF database context
namespace JobTrack.Concrete
{
public class EFDbContext : DbContext
{
//Set the entity framework database context to the connection name
//in the Webconfig file for our SQL Server data source QSJTDB1
public EFDbContext() : base("name=EFDbConnection")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Remove the tight dependency on the entity framework that
//wants to take control of the database. EF by nature wants
//to drive the database so that the database changes conform
//to the model changes in the application. This will remove the
//control from the EF and leave the changes to the database admin
//side so that it continues to be in sync with the model.
Database.SetInitializer<EFDbContext>(null);
//Remove the default pluaralization of model names
//This will allow us to work with database table names that are singular
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
//Allows for multiple entries of the class State to be used with
//interface objects such as IQueryTables for the State database table
public DbSet<State> State { get; set; }
}
}

How to recreate database in EF if my model changes?

I created a DbContext like so :
public class myDB : DbContext
{
public DbSet<Party> Parties { get; set; }
public DbSet<Booking> Bookings { get; set; }
}
This generated my DataBase and the two tables above..Great
I then decided to add another DbSet into the mix & I got an error:
the model backing the 'Party' context has changed since the database was created
I'm aware of the fixes for this using modelbuilder.IncludeMetadataInDatabase = false; and Database.SetInitializer<ClubmansGuideDB>(null);
1) What's the correct way to add my new classes to the context and have them generated in the DataBase?
2) In my futile attempts to solve this myself I deleted the tables and tried to re-run the app with no success I then deleted the full database and re-run and it doesn't re-generate the DB. How can I start from scratch - is there some cache somewhere?
I believe you're looking for:
Database.SetInitializer<ClubmansGuideDB>(new DropCreateDatabaseIfModelChanges<ClubmansGuideDB>());
As of EF 4.1 and above.
Note that this assumes you have permission to even drop your database. I do this locally for ease of development but disable it in staging/production (I do a manual Schema Compare and push my changes).
By the way, for testing, if you need to force recreate the database, you can do:
using (var context = new ClubmansGuideDB()) {
context.Database.Initialize(force: true);
}
(using if you don't already have a reference to your DB)
You can let the Entity Framework recreate the whole database by using a Database Initializer or if you want to migrate data you can look at the Code First Migrations
The following would recreate your database when the model changes:
Database.SetInitializer<myDB>(new DropCreateDatabaseIfModelChanges<myDB>());
Try putting this in your Global.asax.cs Application_Start() method.
Database.SetInitializer<DatabaseContext>(null);
To reset the database from scratch on app run make a class like this
//Where myDB is your context
public class EntityBase: DropCreateDatabaseAlways<myDB>
{
}
Then in your Application_Start() method you can do this
Database.SetInitializer(new EntityBase());

Categories