For our project, we are using the Entity Framework (Version 6) with the code first database. So, when we want to change a procedure or a table, we do that in a class and generate a Migration File to update the Database (simple Update-Database in the Paket-Manager window).
If we want to change something that didn't get a class (like a View or a procedure) we change the migration file, which will look like this as an example:
public override void Up()
{
//Some other code...
Sql("ALTER VIEW ExampleView AS Select [Endless Lines of code]");
}
When it comes to bigger views, it gets very messy very fast.
My Question is
Is there a "smart" way to update small things in a procedure or maybe a view (like changing something in the FROM Statement) without creating a whole SQL statement that counts many rows just to do that?
Not sure what would qualify as "smart", but you can remove the SQL statement clutter from your migration classes by putting them in separate files. This article explains how.
Related
I want to keep a history of changes made to all of my tables generated by EF Core.
I could do this by manually creating a second model for every model I want duplicated, but I was wondering if there's any way to do this automatically somehow.
What I would want it to do is, every time a migration is generated that includes a CreateTable call, create a duplicate of the table with something like "History" appended to the name, and with an additional column relating to the original record.
I'm not sure what would be the best way to achieve this, as I'm not very experienced with ASP.NET yet. I was thinking of overriding the MigrationBuilder.CreateTable method, but I can't figure out how I'd get the actual migration builder to use my overridden method.
Note that SQL does support Temporal Tables which internally create a second table for you. It has start/end time columns instead of a single column. The database engine maintains this for you (meaning even ad hoc sql queries not through EF are properly tracked into the history table). This is part of the ANSI SQL standard so you might find this easier than rolling your own. You can read more about these on the MSFT documentation page
If I understand it correctly, you want to execute custom SQL script before running the migration.
Well, that is indeed possible.
You will generate the migration as you do now and you'll get something like this:
public partial class MyMigration: Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
//auto-generated mappings
}
}
before the auto-generate call, you could insert your own SQL script like this:
public partial class MyMigration: Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("CREATE TABLE Customers_History (CustomerName, City, Country)");
migrationBuilder.Sql("INSERT INTO Customers_History (CustomerName, City, Country) SELECT CustomerName, City, Country FROM Customers)");
//auto-generated mappings
}
}
This is just an example, but you can add your own SQL to match your scenario.
With this solution you are not required to create additional models.
I'm creating a SQL View on my Configuration Seed
protected override void Seed(QuiverContext context)
{
context.Database.ExecuteSqlCommand(DatabaseScripts.Views.MyView);
}
Now I want to add a DBSet to my DbContext that represents my View. I read that one can do this using then the Entity like a regular table.
So I tried but it requires me to add a migration which I did, but then the update-database command fails when creating the view since a table is created first.
It looks like you're trying to create a view in your Seed method. This isn't the way to create a view (remember the seed method runs every time ANY migration runs).
The better way to be would be to add a migration. This will create a code file containing CreateTable lines, which will make your table. Just remove these lines, and replace them with a call to create your view.
You can execute custom Sql inside a migration using the Sql command, for example...
Sql("CREATE VIEW myView.....");
If you want to make things a bit more robust, you can create an extension for migrations which allows you to call CreateView.
I think there were several similar question that I have now, but I am really confused about googling and reading them now. As I have a Code First approach, and I added migration support, updated to a database, and now I need some nasty stuff, like triggers, stored procedures and views. As far as I understand this, I need to write the SQL create triggers and create stored procedure as strings into my C# Code First code. But where? Where I need to add them (as static or const strings)? Need I write the drop triggers/stored procs strings as well? And how to integrate them into the next migration step? Do anybody know a really helpful step-by-step blog about this topic?
I got an advice as generate the next migration step with the "add-migration" command, then update the Up() and Down() methods with the trigger definitions. That's clear but a little bit away from the code first's point of view, I am afraid of that the table definition and the table triggers (and stored procedures) will be separated. Another advice says to override the context OnModelCreating()... but I can't see when it will execute, how to link to a specific migration step...
And please do not argue about "using a trigger is a stupid thing", as my question is wider than this... how to add any advanced sql server "object" to a code first which is not easy to define in C# as a code first?
I had a similar problem recently and the best solution I found was to run the script from an (initially) empty migration. I put the script in a file and added it to the project as a resource.
One interesting trick I had to do is to put special separators in the script file because the GO statement is not a T-SQL statement. I used the term GO--BATCH-- as a batch separator so that it works both in SQL Server Management studio and in code. In code I simply split the script by this separator and run multiple queries like this:
public partial class CodeHostDiscovery : DbMigration
{
public override void Up()
{
var batches = Properties.Resources.CodeHostDiscoverySqlScript.Split(new string[] {"GO--BATCH--"}, StringSplitOptions.None);
foreach (var batch in batches)
{
Sql(batch);
}
}
public override void Down()
{
}
}
Here is a snippet from the SQL script:
CREATE SCHEMA SystemServices
GO--BATCH--
CREATE TABLE [SystemServices].[HeartbeatConfiguration] (
I don't expect Code First to provide better facilities to do this, because the idea behind Code First is that you don't need stored procedures, triggers or anything else. You just use Code First. That doesn't always hold water of course, and for that you can run SQL on the database.
I have two tables that have the same layout -
Report Table
ID
ReportCol1
ReportCol2
In another database I have
Reporting Table
ID
ReportCol1
ReportCol2
I want to use a single entity model called Report to load the data from both of these tables.
In my context class I have
public DbSet<Report> Report{ get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ReportMap());
}
In my call to the first database Report table I get the results as expected.
I change the connection string to point to the second database, but I can't change the name of the table in the table mapping.
I don't want to use stored procs for the reason outlined in my comment.
What can I do, short of the tables names in the database(that is not an option).
Have you tried this fluent API modelBuilder.Entity<Report>().ToTable("Reporting"); ? You may need to write this so it conditionally does this based on which database you are connecting to. You may need to have your configuration allow you to say "DatabaseA uses this mapping and connection string", and "DatabaseB uses this other mapping and conenctions string", and rather than changing the connection string, you specify which database by some name/key, and your app looks up that name to determine which mapping code to run.
if(dbMappingconfig == DbMapping.A)//some enum you create
{
modelBuilder.Entity<Report>().ToTable("Reporting");
}
If your goal is to be able to pass these entities to other methods like DisplayReport(Report r) so that you don't have to duplicate code, you could have both Reporting and Report classes implement a IReport interface.
EF also supports inheritance hierarchies, so you could have them inherit from the same class, BUT I havfe a strong feeling that will not work across databases.
If the OnModelCreating doesn't rerun, it's probably already cached. Put modelBuilder.CacheForContextType = false; in there so it doesn't cache it in future, and to clear the current cache I think you can just do a Clean+Rebuild. This will come at the price of rebuilding the model everytime instead of reusing a cache. What you'd really want is use the cache up until the connection string changes. I don't know of anyway to manually clear the cache, but there might be a way. You can manage the model building yourself:
DbModelBuilder builder = new DbModelBuilder();
// Setup configurations
DbModel model = builder.Build(connection);
DbCompiledModel compiledModel = model.Compile();
DbContext context = new DbContext(connection, compiledModel);
But that will introduce additional complexities since you will need to manage the caching yourself.
While searching on this, I came across this that looks like they are trying to accomplish the same thing, as well as having gone down the same page, see Final section in question: How to map an Entity framework model to a table name dynamically
Are you able to create the same named view in each database and map to that instead of a variable table name?
I have 2 copies of tables with different names in my solution and deal with that by having 2 contexts and 2 sets of map files (generated text templates)
My code is giving me an EntityCommandExecutionException when i'm trying getting data from my Bieren Table.
The exception message says that it can't find "dbo.Biers" which is quite obvious because it's called "dbo.Bieren".
I can quite easily fix this by just renaming the table in the database. Altough i don't like fixing my database around my code's errors.
How can i make the entity framework use the correct table instead of changing the name of my table?
Thanks in advance.
Decorate your entity class with the TableAttribute property, e.g.
[Table("Bieren")]
For the database-first approach, StriplingWarrior's solution works well. But if you use the code-first approach, you could use System.ComponentModel.DataAnnotations.TableAttribute on a target entity class to map an entity class to a database table.
However, this way is slightly annoying, because we commonly want to define a pure entity class. To do this, you could entrust it to another class or use the overrided OnModelCreating method in the DbContext class as the following.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>()
.ToTable("MyCategories");
}
Your Entity Framework model has information about the table names and connections that it is connecting to. If you're using a database-first approach, you should have an EDMX file with your database model in it. The easiest way is to update the model from the database.
Open the EDMX file in Visual Studio
Delete the incorrect "Biers" table
right-click in the background and click "Update Model from database"
Point the wizard at your database, and select the "Bieren" table to add
Finish the wizard, and the "Bieren" table should appear
It is also possible to dig into the properties of the Biers type, and manually change the table name.