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!!
Related
I just wondering if you can store a function in a model (CRUD transactions)
that will look something like this:
My Existing code:
public class tbluser
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
[Required(ErrorMessage = "Username is required")]
public string username { get; set; }
[Required(ErrorMessage = "Password is required")]
public string password { get; set; }
public static List<tbluser> list()
{
using (var db = new sample())
{
var user = db.tbluser.ToList();
return user;
}
}
}
What i want:
public class tbluser:DbContext
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
[Required(ErrorMessage = "Username is required")]
public string username { get; set; }
[Required(ErrorMessage = "Password is required")]
public string password { get; set; }
public static List<tbluser> list()
{
return this.toList();
}
}
I just want to ask also if that method of implementing Entity Framework is ok.
Here is a quick example of how you might setup a simple Code First implementation to get started.
First, define your User model. The Key attribute on an integer type automatically configures the identity property for you. Then, you may want an index on username if you plan to do frequent lookups by username (to get user details or to validate a password).
public class User
{
[Key] // Becomes identity by default
public int Id { get; set; }
[Index("IX_User_Username", IsUnique = true)]
public string Username { get; set; }
public string Password { get; set; }
}
Then, you can define
public class AppDataContext : DbContext
{
public AppDataContext() : base("name=DBConnection") { }
public DbSet<User> Users { get; set; }
}
You will just need to be sure there is a connection string in your config file to match the name passed there.
<connectionStrings>
<add name="DBConnection" providerName="System.Data.SqlClient"
connectionString="Data Source=instancePath;Initial Catalog=dbName;Integrated Security=true;MultipleActiveResultSets=True" />
</connectionStrings>
This would now allow you to create repos such as this:
public class UserRepo : IDisposable
{
public Lazy<AppDataContext> _db = new Lazy<AppDataContext>(() => new AppDataContext());
public IQueryable<User> Get() => _db.Value.Users.AsQueryable();
public IList<User> GetAll() => _db.Value.Users.ToList();
public void Dispose()
{
if (_db.IsValueCreated)
_db.Value.Dispose();
}
}
So then you can either use the repo or the context directly.
// Use the repo
using (var userRepo = new UserRepo())
{
var allUsers = userRepo.GetAll();
var user = userRepo.Get().FirstOrDefault(m => m.Username == "myUsername");
}
// Or just use the data context
using (var db = new AppDataContext())
{
var allUsers = db.Users.ToList(); // Get all users
var user = db.Users.FirstOrDefault(m => m.Username == "myUsername");
}
For more information, here are some useful links with great details:
Simple Example
Data Annotations
Initializer Config
Migrations
Code like this is going to be heavily problematic.
In the first example you are tightly coupling an instance of a DbContext to an entity. Calling tblUser.list() will return a list of User entities, but these will now be outside of the scope of a DbContext. (Due to the using() block closure) This means that any lazy load calls to retrieve related entities will fail and you cannot persist any changes to the entities until they are re-attached to another DbContext. This gets very messy, very fast.
In the second example you would be extending a DbContext, meaning each "entity" is effectively scoping a DbContext use to populate instances of itself. You can't just "static" wrap the method because that wouldn't have visibility to the non-static DbSets inherited from DbContext.
This would be horrible in terms of performance, and from a code perspective would look plain weird:
I.e.
using (var user = new tbluser)
{
var users = user.list(); // not static.
// .. Do stuff..
}
To make it static would be problematic because a DbContext would need to be static-scoped inside tbluser
public class tbluser
{
private static MyContext _context = new MyContext();
// ...
public static List<tbluser> list()
{
return _context.tblusers.ToList();
}
}
And this may still have issues, such as how the static instance is disposed, before it was remotely functional but I certainly cannot recommend an approach like this.
Instead, use the DbContext as it is intended. Look at IoC containers like Unity or Autofac to manage the lifetime scope for for the DbContext and inject an instance as a dependency into classes that need it, or at a minimum wrap it in a using() {} block and treat it like an repository with it's DbSets.
There are lots of examples of using the DbContext effectively, using Repositories and Unit of Work patterns with dependency injection. Master these before attempting to spin up something unique. Future developers looking at your code will thank you. :)
There is one famous principle called "Separation of Concerns" that will get very angry if you do this. My advice is to keep the code simple, meaningful and loosely coupled.
Hi I have server with some databases that have the same schema. I use EF6 Database/Model First code and I do not want to create deterrent DbContext for them. for example my generated DbContext is :
public partial class TEST_Rev5_FINALEntities : DbContext
{
public TEST_Rev5_FINALEntities()
: base("name=TEST_Rev5_FINALEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<Header> tbl_Headers { get; set; }
public virtual DbSet<Output> tbl_Output { get; set; }
public virtual DbSet<Run> tbl_Run { get; set; }
}
and I created a partial class to set the connection string
public partial class TEST_Rev5_FINALEntities : DbContext
{
public TEST_Rev5_FINALEntities(DbConnection dbConnection)
: base(dbConnection, true)
{
}
}
And I have the following method to create the connection with deterrent connection string:
public DbConnection GetConnectionString()
{
DbConnection conn;
SqlConnectionStringBuilder sqlConnectionStringBuilder = new SqlConnectionStringBuilder
{
DataSource = DataSource,
IntegratedSecurity = false,
UserID = User,
Password = Password,
MultipleActiveResultSets = true
};
SqlConnectionFactory sqlConnectionFactory = new SqlConnectionFactory(sqlConnectionStringBuilder.ConnectionString);
conn = sqlConnectionFactory.CreateConnection(DatabaseName);
return conn;
}
Finally I try to run it like this:
using (var context = new TEST_Rev5_FINALEntities(_dal.Connector.GetConnectionString()))
{
return context.tbl_Headers.FirstOrDefault();
}
but I get this error :
System.Data.Entity.Infrastructure.UnintentionalCodeFirstException
HResult=0x80131509 Message=The context is being used in Code First
mode with code that was generated from an EDMX file for either
Database First or Model First development.
How can I do it?
The behavior EF uses depends on the way your connection string looks. If it includes a metadata attribute like this:
metadata=res://*/model.csdl|res://*/model.ssdl|res://*/model.msl;
It will presume you are using Database or Model first development.
To make sure Code First is used, remove metadata part of the connection string.
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
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
So I am trying to build Custom membership using EF. I dont really know what i am doing but it has gone fairly smooth so far.
I am on the Database Initializer step where i am trying to dump data into the database soon as the project runs. My class Application.cs uses a GUID as a primary key as seen below. I am trying to figure out how i can add that GUID into the database.
I don't know if this is possible but this is what i am trying to do. I took the default login's Database you get when you make a normal web application project in VS 2012 and trying to recreate that database using EF Code First(For practice). This is what i got so far.
Class
public class Applications
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ApplicationId { get; set; }
[StringLength(125)]
public string ApplicationName { get; set; }
}
Intializer to dump data into db on build(not including Seed.)
private static List<Applications> addApplications()
{
var apps = new List<Applications>
{
new Applications
{
ApplicationId = Guid.NewGuid(),
ApplicationName = "Test Login"
}
};
return apps;
}
private static List<Memberships> addMemberships()
{
var mem = new List<Memberships>
{
new Memberships
{
UserId = Guid.NewGuid(),
ApplicationId = ?, // How can this guid Match the one in
// in ApplicationId for the Application Table?
}
};
return mem;
}
I get "Invalid Initializer member declarator". The problem i face is that I need the GUIDS to be the same for ApplicationId across multiple tables. I don't even know if this is possible or right?
I got a feeling I have to share it somehow maybe like
Guid AppId;
AppId = Guid.NewGuid();
In your Membership model instead of storing the GUID "ApplicationId" to try and reference the application you should use a navigation property like so (see this link for better description of navigation properties):
public class Memberships
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UserId { get; set; }
//if you set your model up like this entity framework will take care of creating
/the foreign key for you.
public Application MemberApplication { get; set; }
}
then just pass in the appropriate application to your method like so:
private static List<Memberships> addMemberships(Application app)
{
var mem = new List<Memberships>
{
new Memberships
{
UserId = Guid.NewGuid(),
Application = app,
}
};
return mem;
}
Setting your model up like this lets you take full advantage of oop and relational database. Hope that helps.