【问题标题】:Generating database per tenant using EF C#使用 EF C# 为每个租户生成数据库
【发布时间】:2018-11-07 21:50:26
【问题描述】:

我正在使用C# EF Code First approach 构建一个多租户应用程序。

我选择为每个租户使用单独的数据库。在这方面,当应用程序加载时,租户可以登录或注册。

这是注册页面的样子。

在提交时,我在名为 MyAppDB 和表 tblTenants 的公共数据库中捕获此租户详细信息

这里的租户名称是唯一的,成功提交表单后,我需要使用租户名称在同一台服务器上创建新数据库并为模型生成一组表。

这是每个租户的模型。

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;}
}

如何使用 C# EF Code First 方法将此模型生成到表中??

谢谢。

【问题讨论】:

  • 您可以创建database programmatically。你应该这样做是一个不同的问题。
  • @FilipCordas,请验证我的回答

标签: c# asp.net entity-framework ef-code-first


【解决方案1】:

您似乎有两个问题。

  1. 如何在 SQL Server 上以编程方式创建数据库?

    如果您不希望代码中有任何依赖项,您可以直接对您的 sql 服务器运行运行 sql 命令:

    using (var connection = new SqlConnection(myConnectionString))
    {
          connection.Open();
          var command = connection.CreateCommand();
          command.CommandText = "CREATE DATABASE mydb";
          command.ExecuteNonQuery();
    }
    

    Bam 你得到了你的新数据库,你的新数据库的连接字符串将是"Data Source=ServerName;Initial Catalog=mydb;Integrated Security=False;User Id=sa;Password=password;MultipleActiveResultSets=True"

  2. 如何使用 EF 代码优先在新数据库中创建表?

    你不一定,你让 EF 处理这个。

    创建您的tenantdbcontext 类:

    public class TenantDbContext : DbContext
    {
        public TenantDbContext () : Base("name=yourConnString") { }
    
        public DbSet<Product> Products { get; set; }
        public DbSet<Sales> Sales { get; set; }
    }
    

当您需要访问这些表时,在您的代码中使用

TenantDbContext tdc = new TenantDbContext();
tdc.Products.Add(new Product)

您的挑战将是根据与您合作的租户使用适当的连接字符串到适当的上下文。

【讨论】:

  • 请查看我的答案并验证。谢谢!!
【解决方案2】:

这是我目前如何满足我的要求。

当任何新用户/租户提交表单时,

  1. 首先,我将用户/租户详细信息插入到公共/主数据库中
  2. 然后我在服务器上创建一个带有租户名称的新数据库。

提交操作:-

    [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);
    }

这就是我的 TenantContext 类的样子,它处理主要内容以按需生成数据库。

 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; }
   }}

请随时验证此方法并添加建议。

希望这会有所帮助!

【讨论】:

  • 如果你问我,我不会在构造函数中这样做。并且不要在 sql 中使用字符串连接,尤其是在没有验证的情况下,这对于 sql 注入是不安全的。为什么你的租户的密码在我看来就像一个用户。如果您可以使用现有的识别系统,也不要以纯文本形式存储密码,除非您非常了解安全性。
  • 其他我认为您查看了this 帖子的答案。他们将 DI 用于租户,这就是为什么您在构造函数中有 AppTenant 的原因,您也应该使用 DI 而不是使用 new 创建。
  • @FilipCordas,这只是该方法的框架。 DI 和其他东西将在真正的应用程序中得到照顾
  • 如果没有别的,请删除该 sql 注入部分,以免有人复制粘贴。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-08-23
  • 2020-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-19
相关资源
最近更新 更多