I've been trying for awhile to figure out how to use a single DBContext to create multiple tables in a Code First fashion without any luck. I'm sure it's just my unfamiliarity with the framework but I'm not sure what I'm missing. Here's a simple example with entities and the DBContext.
[Table("MyEntity")]
public class MyEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string MyColumn { get; set; }
public int MyNumber { get; set; }
}
[Table("MySecondEntity")]
public class MySecondEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string MyColumn { get; set; }
public int MyNumber { get; set; }
}
public class MyContext : DbContext
{
public DbSet<MyEntity> MyTable { get; set; }
public DbSet<MySecondEntity> MyTable2 { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionStringBuilder = new SqliteConnectionStringBuilder {DataSource = "test.db"};
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
optionsBuilder.UseSqlite(connection);
}
}
It looks to me like it should work, but when I call it in the below code it blows up with a 'no such table: MyEntity' Sqlite exception when hitting the first foreach loop.
static void Main(string[] args)
{
using (var db = new MyContext())
{
MyEntity testEntity1 = new MyEntity();
MySecondEntity entity1 = new MySecondEntity();
testEntity1.MyColumn = "Test Data 1";
testEntity1.MyNumber = 12345;
db.MyTable.Add(testEntity1);
db.Database.Migrate();
entity1.MyColumn = "New Data 1";
entity1.MyNumber = 2;
db.MyTable2.Add(entity1);
db.Database.Migrate();
Console.WriteLine("Inserting Data...");
Console.WriteLine("Data in the Database");
foreach (var entity in db.MyTable)
{
Console.WriteLine("Id: " + entity.Id);
Console.WriteLine("Column Data: " + entity.MyColumn);
Console.WriteLine("Number: " + entity.MyNumber);
}
foreach (var entity in db.MyTable2)
{
Console.WriteLine("Id: " + entity.Id);
Console.WriteLine("Column Data: " + entity.MyColumn);
Console.WriteLine("Number: " + entity.MyNumber);
}
}
Console.WriteLine("Examples run finished,press Enter to continue...");
Console.ReadLine();
}
I can almost guarantee it's something simple I'm missing but I just can't seem to find it, and there aren't any examples I can find in their documentation. There seems to be a similar issue submitted on GitHub here https://github.com/aspnet/EntityFramework/issues/2874 but that's for multiple contexts. So maybe this is another piece that just hasn't quite made it to release yet?
Solution
By following the tutorial posted on http://ef.readthedocs.org/en/latest/getting-started/uwp.html as suggested by #natemcmaster and the solution recommended by #lukas-kabrt I was able to get it to work as desired. By running the below commands I was able to get the tables created and insert/select data from them.
Install-Package EntityFramework.Commands –Pre
Add-Migration MyFirstMigration
Update-Database
Check out Getting Started on UWP - EF 7 in the official docs. The following notes are from that document.
The default path on UWP is not writable. Your DB file needs to be in ApplicationData.Current.LocalFolder
options.UseSqlite("Data Source=" + Path.Combine(ApplicationData.Current.LocalFolder.Path, "blogging.db"))
Also, take note that on UWP you cannot run migrations from commands. You need to run them in the app.
using (var db = new BloggingContext())
{
db.Database.Migrate();
}
The DbContext class contains configuration of your database - tables, relationsships, etc. To use the DbContext you need to create a database that matches your DbContext. In the Code-first world, it is done by database migrations.
For ASP.NET 5 you need to add this configuration to your project.json file
"commands": {
"ef": "EntityFramework.Commands"
}
and then add a migration to your project and apply this migration to the DB by running following commands
dnx ef migrations add MyFirstMigration
dnx ef database update
For Full .NET you need to run following commands in the Package Manager Console (Tools ‣ NuGet Package Manager ‣ Package Manager Console)
Install-Package EntityFramework.Commands –Pre
Add-Migration MyFirstMigration
Update-Database
Related
Inserting a record to SQLite DB via Entity Framework Core in C# in .NET Core is extremely slow. It is 10 times slower than my expectation. Is there a magic to improve performance?
public class MyRecord
{
[Key]
public int ID { get; set; }
public int Value { get; set; }
}
public class MyDatabase : DbContext
{
public DbSet<MyRecord> Records { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data source=mydb.db");
#if DEBUG
optionsBuilder.EnableSensitiveDataLogging(true);
#endif
}
}
public class MyDatabaseTests
{
public MyDatabaseTests()
{
var db = new MyDatabase();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
}
[TestMethod]
public void EntityFramework1000Transactions()
{
for (int i = 0; i < 1000; i++)
{
using var db = new MyDatabase();
var rec = new MyRecord
{
ID = i+1,
Value = 123456789
};
var tx = db.Database.BeginTransaction();
db.Records.Add(rec);
db.SaveChanges();
tx.Commit();
}
}
}
EntityFramework1000Transactions takes 10 seconds on my PC with SATA-SSD drive. Replacing to eNVM is not my option because I am writing an application for a sort of embedded system which has equivalent storage.
I tried using db.ChangeTracker.AutoDetectChangesEnabled = false;, or
db.BulkSaveChanges(); (with Z.EntityFramework.Extensions.EFCore)
And they didn't help me.
My environment:
Visual Studio 2019 Version 16.11.2
.NET Core 3.1
C#
Microsoft.EntityFrameworkCore.Sqlite 5.0.11
Windows 10
Edit
The loop was a simulation of a process in the application. They are not single transaction. The loop is intended to simulate 1000 transactions. Therefore they cannot be in the same transaction.
It also cannot remove transaction. The transactions in the application is actually more complicate and can update two or more tables/records at the same time. It must be handled in a transaction. therefore removing transaction is not my option.
Limiting the number of connection hits(inside for loop) to database can resolve the problem as mentioned in this thread.
I am trying to insert data from my ASP.NET Core MVC application with Entity Framework to my SQL Server database on localhost.
My model class looks like this:
public class Auto
{
public string Motorleistung { get; set; }
public string Lackierung { get; set; }
public string Felgen { get; set; }
public string Sonderleistungen { get; set; }
}
I already added the DbContext in a new folder (Services/AutoContext class):
public class AutoContext : DbContext
{
DbSet<Auto> Autos { get; set; }
public AutoContext(DbContextOptions<AutoContext> options)
: base(options)
{
Database.EnsureCreated();
}
}
I added this part to the Startup.cs file in the ConfigurateServices method:
public void ConfigureServices(IServiceCollection services)
{
var connectionString = "Server=localhost;Database=Auto;User Id=sa;Password=YourPassword123";
services.AddControllersWithViews();
services.AddDbContext<AutoContext>(o => o.UseSqlServer(connectionString));
}
I am trying to use a class extension with a method to insert properties from auto into the DB:
public static class AutoContextExtensions
{
public static void CreateSeedData(this AutoContext context)
{
var auto = new List<Auto>()
{
new Auto()
{
Motorleistung = "500 PS",
Lackierung = "Gelb",
Felgen = "Chrome",
Sonderleistungen = "Sitzheizung"
}
};
context.AddRange(auto);
context.SaveChanges();
}
}
I am now trying to call the CreateSeedData function from the Startup.cs to pass the data into my database like:
Projectname.AutoContextExtensions.CreateSeedData();
It expects me to give a parameter. What parameter do I have to pass?
(I extracted the code from a sample)
You need an instance of the context to be able to call the extension method.
There are more recommended ways to seed the database in EFCore
1- Use migrations to seed the data
Then EF Core migrations can automatically compute what insert, update
or delete operations need to be applied when upgrading the database to
a new version of the model.
in OnModelCreating in the DBContext
modelBuilder.Entity<Auto>().HasData( new Auto() {
Motorleistung = "500 PS",
Lackierung = "Gelb",
Felgen = "Chrome",
Sonderleistungen = "Sitzheizung"
});
Then Add a new migration
2- Use seeding context
using (var context = new DataSeedingContext())
{
context.Database.EnsureCreated();
var testBlog = context.Blogs.FirstOrDefault(b => b.Url == "http://test.com");
if (testBlog == null)
{
context.Blogs.Add(new Blog { Url = "http://test.com" });
}
context.SaveChanges();
}
a Warning from the docs
The seeding code should not be part of the normal app execution as
this can cause concurrency issues when multiple instances are running
and would also require the app having permission to modify the
database schema.
For more details check the documentation along with full sample project
CreateSeedData is an extension method for AutoContext type.But you are calling method like Projectname.AutoContextExtensions.CreateSeedData(); so you need to pass the parameter value of type autocontext. You should create a variable with AutoContext type ,Then variablename.CreateSeedData() for extension method to work like you expect
I am building a multi-tenant application using C# EF Code First approach.
I am opting for separate DB per tenant. In this regard, when the application loads the tenant can login or register.
This is how registration page looks like.
On submit, I capture this tenant details in the common DB with name MyAppDB & table tblTenants
Here Tenant name is unique & on successful submission of the form, I need to create new DB on the same server with Tenant name & generate set of tables for the Models.
This is my model looks like for every Tenant.
public class Product
{
public int Id { get; set;}
public string Name { get; set;}
}
public class Sales
{
public int Id { get; set;}
public string Name { get; set;}
}
How do I generate this Models to tables using C# EF Code First approach??
Thanks.
Seems like you have two questions.
How do I create a database programmatically on an SQL Server?
If you don't want any dependencies in your code, you can run run sql commands directly against your sql server:
using (var connection = new SqlConnection(myConnectionString))
{
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "CREATE DATABASE mydb";
command.ExecuteNonQuery();
}
Bam you got your new db, the connection string for your new db will be "Data Source=ServerName;Initial Catalog=mydb;Integrated Security=False;User Id=sa;Password=password;MultipleActiveResultSets=True"
How do I create tables in the new db using EF code-first?
You don't necessarily, you let EF deal with this.
Create your tenantdbcontext class:
public class TenantDbContext : DbContext
{
public TenantDbContext () : Base("name=yourConnString") { }
public DbSet<Product> Products { get; set; }
public DbSet<Sales> Sales { get; set; }
}
In your code when you need to access those tables use
TenantDbContext tdc = new TenantDbContext();
tdc.Products.Add(new Product)
Your challenege will be in using the appropriate connection strings to appropriate contexts according to which tenant you are working with.
Here is how I have addressed my requirement currently.
When any new user/tenant submits the form,
First, I am inserting user/tenant details into common/master DB
Then I create a new DB with Tenant Name on the server.
Submit Action:-
[HttpPost]
public ActionResult CreateTenant(Tenanat tenanat)
{
//Master/Common DB
using (DataContext ctx = new DataContext())
{
ctx.Tenanats.Add(tenanat);
ctx.SaveChanges();
string con = ConfigurationManager.ConnectionStrings["tenantContext"].ConnectionString;
con = con.Replace("tenDbName", tenanat.Name.Trim());
TenantContext tenantContext = new TenantContext(con,tenanat);
}
return RedirectToAction("Index", "Tenanat",tenanat);
}
This is what my TenantContext class looks like which is handling the primary stuff to generate the DB on demand.
public class TenantContext : DbContext
{
public TenantContext(string connection, Tenanat tenanat)
: base(connection)
{
//create DB for tenant
Database.CreateIfNotExists();
//create user for the new Db
string sql = "CREATE LOGIN \"" + tenanat.Email + "\" WITH PASSWORD = '" + tenanat.Password + "'; USE " + tenanat.Name + "; CREATE USER \"" + tenanat.Email + "\" FOR LOGIN \"" + tenanat.Email + "\" WITH DEFAULT_SCHEMA = dbo;";
Database.ExecuteSqlCommand(sql);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
}}
Please feel free to validate this approach & add suggestions.
Hope this helps!!
I keep getting the following error:
System.InvalidOperationException: The model backing the 'McContext'
context has changed since the database was created. Consider using
Code First Migrations to update the database
(http://go.microsoft.com/fwlink/?LinkId=238269).
I am creating an .NET MVC 5 website using the latest Entity Framework from nuget, v6.1.3. I am attached to a local MDF file as I don't have SQL Server or Express installed locally. I am using the Code First model.
I spent hours yesterday and today combing through SO and WWW posts on this error, but have not found a solution that works. Most of them revolve around different types of Initializers, or short explanations about how code first requires migrations or initializers for model changes. But I am running the setup manually through the PM console. I have included my steps below.
Connection String:
<add name="testSQLConnection" connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\jwatts\Documents\Visual Studio 2015\Projects\MyProject\Databases\MyProject01.mdf;Integrated Security=True;Connect Timeout=30" providerName="System.Data.SqlClient" />
The model:
public class Family
{
public Family() { }
[Key, Index(IsUnique = true)]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[StringLength(128)]
public string Name { get; set; }
[StringLength(1024)]
public string Description { get; set; }
public DateTime Created { get; set; }
public string CreatedBy { get; set; }
public DateTime? Changed { get; set; }
public string ChangedBy { get; set; }
public DateTime? Deleted { get; set; }
public string DeletedBy { get; set; }
}
The Context:
public class McContext : DbContext
{
public McContext() : base("testSQLConnection") { }
public DbSet<Family> Families { get; set; }
}
The configuration:
internal sealed class Configuration : DbMigrationsConfiguration<Data.McContext>
{
public Configuration()
{
MigrationsDirectory = #"Data\Migrations";
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = false;
}
protected override void Seed(McContext context)
{
context.Families.Add(new Family()
{
Name = "My Family",
Description = "The family",
Created = DateTime.Now,
CreatedBy = "system"
});
context.SaveChanges();
}
}
Auto generated migration class:
public partial class StartupConfig : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Families",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: 128),
Description = c.String(maxLength: 1024),
Created = c.DateTime(nullable: false),
CreatedBy = c.String(),
Changed = c.DateTime(),
ChangedBy = c.String(),
Deleted = c.DateTime(),
DeletedBy = c.String(),
})
.PrimaryKey(t => t.Id)
.Index(t => t.Id, unique: true);
}
public override void Down()
{
DropIndex("dbo.Families", new[] { "Id" });
DropTable("dbo.Families");
}
}
Unit test code:
[TestMethod]
public void CreateTheContext_Test()
{
using (var ctx = new McContext())
{
//error on this line below:
var fam = ctx.Families.Where(f => f.Name.Contains("My")).FirstOrDefault();
Assert.IsTrue(fam != null && fam.Name.Contains("My"));
}
}
Recreation steps:
I started with a "clean slate" and deleted all database tables in my test database, and deleted all Data\Migrations.
Rebuilt the solution. (The base project and a unit testing project)
In Package Manager Console, ran "Add-Migration StartupConfig"
A migration file is added to Data\Migrations called "{datetime}_StartupConfig.cs". See StartupConfig class above.
I built the project.
I ran "Update-Database -TargetMigration:StartupConfig"
The PM console outputs 4 lines, including the "Running Seed method", and completes successfully.
I confirmed via SQL query that the "__MigrationHistory" and "Families" table have been added to the database, and each table has 1 record.
I ran the unit test and receive the error.
I had a more complex model, but I began removing things one by one making things as simple as I could. But I can't get any simpler than this, and yet the error persists. Of note, for this particular project I have never gotten a successful query out of the unit test.
As I was typing out these recreation steps above, I was performing the steps at the same time, and got the same error.
I am at a loss as to what to try next?
Since you'r code it's not changing it has to be something with the Migrations table (the one that has info of your db model) or with the DB file you're using.
I will suggest to install Sql server express and try everything again. Since the database you're making is a file you will have more risk with it.
Edit:
Try to see the name of the instances your projects are using. By default EF use DefaultConnection. Check the web.config or configuration files in all your projects.
In checking the connection string in the Unit Test per #AaronLS suggestion, I found that the connection string was different.
Turns out, the App.config for the Unit Test did not have the connection string and was treating "sqlTestConnection" as the database name since it could not resolve it otherwise.
Adding the connection string to the App.config resolved the issue.
I have some problem with this code:
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
using (var context = new MyContext()) {
var newType = new SysType { Name = "New Name" };
context.SysTypes.Add(newType);
context.SaveChanges();
}
using (var context = new MyContext()) {
Console.WriteLine(context.SysTypes.FirstOrDefault());
}
Console.ReadLine();
}
}
public class SysType
{
public int Id { get; set; }
public string Name { get; set; }
}
public class MyContext : DbContext
{
public MyContext() : base("name=MyDb") { }
public DbSet<SysType> SysTypes { get; set; }
}
During execution the program an exception happens:
System.Data.Entity.Infrastructure.DbUpdateException"
System.Data.SqlClient.SqlExeption: Ad hoc updates to system catalogs are not allowed.
But the database does get created (to post images not allowed for me)
![SQL Server Object Explorer view]
If try to query a table's data, an exception is thrown
![Exception if query table data]
If I create tables manually using a SQL script, then table query is successful.
Аfter many hours, I realized that this is because of the name of the table.
If change table name - all work perfectly.
My question is: why does my table name SysType throw an exception? Аnd is there any other names of the tables that cause a similar problems?
I will be glad to hear the answer to my question
Update: Model First approach - the same result
Because the entity you have will be created as dbo.SysTypes where as it is an existing system table in the database that is also dbo.SysTypes.
Your entity will be generated as dbo.SysTypes because dbo is the default schema name when generating a table and the SysType will be pluralized into SysTypes by PluralizingTableNameConvention.
The solution is to configure manually the generated table name, you can either using attribute:
[TableAttribute("Application_SysTypes")]
or using fluent api:
modelBuilder.Entity<SysType>().ToTable("Application_SysTypes")`
systypes is a system table in SQL Server: http://technet.microsoft.com/en-us/library/aa260587(v=sql.80).aspx
A list of all system tables: http://technet.microsoft.com/en-us/library/aa260604(v=sql.80).aspx