c# entity - متعدد المستأجر مع الرمز الأول EF6





framework crud (5)


حسنًا ، إذا لم يكن رمزًا أولاً ، سأحاول فعله بهذه الطريقة:

  • توليد الجداول في المخطط الافتراضي ، ويقول dbo

  • توليد edmx على أساس قاعدة البيانات الموجودة

  • تأخذ EF مع POCO كما قالب TT ابتداء

  • تحرير ملف TT وإضافة خاصية جديدة إلى السياق تسمى schema وسيتم فرض الاستعلامات في الفئات التي تم إنشاؤها لاستخدامها في كائنات قاعدة البيانات.

بهذه الطريقة ستتمكن من إنشاء سياقات للمخططات المختلفة وحتى السماح للكائنات بالتحليق بين السياقات.

تحتاج منظمتنا إلى قاعدة بيانات واحدة ، متعددة المستأجرين
(من خلال مخطط الجدول ، وليس حسب معرف المستأجر ).

هناك مقالة رائعة هنا للبدء في هذا النوع من الأشياء هنا: http://romiller.com/2011/05/23/ef-4-1-multi-tenant-with-code-first/

في منتصف المقال ، هذا مكتوب:

ستلاحظ (ربما مع بعض الفزع) نحتاج إلى كتابة التعليمات البرمجية لتكوين مخطط الجدول لكل كيان. من المسلم به أنه لا يوجد الكثير من الأحبار السحرية التي ترعى هذا الرمز ... في الإصدارات المستقبلية من EF ، سنكون قادرين على استبدال ذلك باتفاقية مخصصة أكثر نظافة.

يتمثل هدفنا في الحصول على أنظف طريقة ممكنة للحصول على فئة سياق واحدة يمكننا استخدامها للاتصال بمخططات متعددة لها نفس الطراز.
( لاحظ أن modelBuilder.HasDefaultSchema لا يبدو كافيا ، لأنه لا ينطبق إلا في المرة الأولى التي يهيئ فيها EF السياق ويدير OnModelCreating)

هل توجد الاتفاقية المخصصة الأنظف المذكورة أعلاه في EF5 أو EF6؟
أم أن هناك طريقة أنظف للتعامل مع هذا بطريقة ما؟

ملاحظة: لقد طرحت هذا السؤال أيضًا على منتدى التطوير ، حيث يبدو أنه يتعلق أكثر باتجاه EF ، لكنه أراد أن يرى ما إذا كان لدى أي شخص هنا بدائل.

ملاحظة 2: لست قلقاً بشأن عمليات الترحيل ، سنقوم بمعالجة ذلك بشكل منفصل.




الخاصية modelBuilder.HasDefaultSchema في OnModelCreating كافية إذا قمت بتطبيق IDbModelCacheKeyProvider على DbContext الخاص بك. يتم إنشاء نموذج مرة واحدة EntityFramwork مؤقتًا داخليًا بواسطة EntityFramwork ويمكنك تحديد المفتاح الخاص بك لذاكرة التخزين المؤقت. خذ اسم مخطط كمفتاح نموذج ذاكرة التخزين المؤقت وسوف يقوم EF بإنشاء نموذج من خلال كل مفتاح ذاكرة التخزين المؤقت المختلفة (المخطط في حالتنا). هنا دليلي على رمز المفهوم:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using TenantDataModel;

namespace TenantDataContext
{
    public class TenantDataCtx : DbContext, IDbModelCacheKeyProvider
    {
        #region Construction

        public static TenantDataCtx Create(string databaseServer, string databaseName, string databaseUserName, string databasePassword, Guid tenantId)
        {
            var connectionStringBuilder = new System.Data.SqlClient.SqlConnectionStringBuilder();
            connectionStringBuilder.DataSource = databaseServer;
            connectionStringBuilder.InitialCatalog = databaseName;
            connectionStringBuilder.UserID = databaseUserName;
            connectionStringBuilder.Password = databasePassword;

            string connectionString = connectionStringBuilder.ToString();
            return new TenantDataCtx(connectionString, tenantId);
        }

        // Used by EF migrations
        public TenantDataCtx()
        {
            Database.SetInitializer<TenantDataCtx>(null);
        }

        internal TenantDataCtx(string connectionString, Guid tenantId)
            : base(connectionString)
        {
            Database.SetInitializer<TenantDataCtx>(null);
            this.SchemaName = tenantId.ToString("D");
        }

        public string SchemaName { get; private set; }

        #endregion

        #region DataSet Properties

        public DbSet<TestEntity> TestEntities { get { return this.Set<TestEntity>(); } }

        #endregion

        #region Overrides

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            if (this.SchemaName != null)
            {
                modelBuilder.HasDefaultSchema(this.SchemaName);
            }

            base.OnModelCreating(modelBuilder);
        }

        #endregion

        #region IDbModelCacheKeyProvider Members

        public string CacheKey
        {
            get { return this.SchemaName; }
        }

        #endregion
    }
}

وعلاوة على ذلك وجدت طريقة لاستخدام هجرة EF. أنا لست سعيدًا حقاً بحلتي ، لكن يبدو أنه لا توجد حلول أخرى متاحة الآن.

using System;
using System.Collections.Generic;
using System.Data.Entity.SqlServer;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TenantDatabaseManager
{
    public class SqlServerSchemaAwareMigrationSqlGenerator : SqlServerMigrationSqlGenerator
    {
        private string _schema;

        public SqlServerSchemaAwareMigrationSqlGenerator(string schema)
        {
            _schema = schema;
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.AddColumnOperation addColumnOperation)
        {
            string newTableName = _GetNameWithReplacedSchema(addColumnOperation.Table);
            var newAddColumnOperation = new System.Data.Entity.Migrations.Model.AddColumnOperation(newTableName, addColumnOperation.Column, addColumnOperation.AnonymousArguments);
            base.Generate(newAddColumnOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.AddPrimaryKeyOperation addPrimaryKeyOperation)
        {
            addPrimaryKeyOperation.Table = _GetNameWithReplacedSchema(addPrimaryKeyOperation.Table);
            base.Generate(addPrimaryKeyOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.AlterColumnOperation alterColumnOperation)
        {
            string tableName = _GetNameWithReplacedSchema(alterColumnOperation.Table);
            var newAlterColumnOperation = new System.Data.Entity.Migrations.Model.AlterColumnOperation(tableName, alterColumnOperation.Column, alterColumnOperation.IsDestructiveChange);
            base.Generate(newAlterColumnOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.DropPrimaryKeyOperation dropPrimaryKeyOperation)
        {
            dropPrimaryKeyOperation.Table = _GetNameWithReplacedSchema(dropPrimaryKeyOperation.Table);
            base.Generate(dropPrimaryKeyOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.CreateIndexOperation createIndexOperation)
        {
            string name = _GetNameWithReplacedSchema(createIndexOperation.Table);
            createIndexOperation.Table = name;
            base.Generate(createIndexOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.CreateTableOperation createTableOperation)
        {
            string newTableName = _GetNameWithReplacedSchema(createTableOperation.Name);
            var newCreateTableOperation = new System.Data.Entity.Migrations.Model.CreateTableOperation(newTableName, createTableOperation.AnonymousArguments);
            newCreateTableOperation.PrimaryKey = createTableOperation.PrimaryKey;
            foreach (var column in createTableOperation.Columns)
            {
                newCreateTableOperation.Columns.Add(column);
            }

            base.Generate(newCreateTableOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.RenameTableOperation renameTableOperation)
        {
            string oldName = _GetNameWithReplacedSchema(renameTableOperation.Name);
            string newName = renameTableOperation.NewName.Split(new char[] { '.' }).Last();
            var newRenameTableOperation = new System.Data.Entity.Migrations.Model.RenameTableOperation(oldName, newName, renameTableOperation.AnonymousArguments);
            base.Generate(newRenameTableOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.RenameIndexOperation renameIndexOperation)
        {
            string tableName = _GetNameWithReplacedSchema(renameIndexOperation.Table);
            var newRenameIndexOperation = new System.Data.Entity.Migrations.Model.RenameIndexOperation(tableName, renameIndexOperation.Name, renameIndexOperation.NewName);
            base.Generate(newRenameIndexOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.AddForeignKeyOperation addForeignKeyOperation)
        {
            addForeignKeyOperation.DependentTable = _GetNameWithReplacedSchema(addForeignKeyOperation.DependentTable);
            addForeignKeyOperation.PrincipalTable = _GetNameWithReplacedSchema(addForeignKeyOperation.PrincipalTable);
            base.Generate(addForeignKeyOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.DropColumnOperation dropColumnOperation)
        {
            string newTableName = _GetNameWithReplacedSchema(dropColumnOperation.Table);
            var newDropColumnOperation = new System.Data.Entity.Migrations.Model.DropColumnOperation(newTableName, dropColumnOperation.Name, dropColumnOperation.AnonymousArguments);
            base.Generate(newDropColumnOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.RenameColumnOperation renameColumnOperation)
        {
            string newTableName = _GetNameWithReplacedSchema(renameColumnOperation.Table);
            var newRenameColumnOperation = new System.Data.Entity.Migrations.Model.RenameColumnOperation(newTableName, renameColumnOperation.Name, renameColumnOperation.NewName);
            base.Generate(newRenameColumnOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.DropTableOperation dropTableOperation)
        {
            string newTableName = _GetNameWithReplacedSchema(dropTableOperation.Name);
            var newDropTableOperation = new System.Data.Entity.Migrations.Model.DropTableOperation(newTableName, dropTableOperation.AnonymousArguments);
            base.Generate(newDropTableOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.DropForeignKeyOperation dropForeignKeyOperation)
        {
            dropForeignKeyOperation.PrincipalTable = _GetNameWithReplacedSchema(dropForeignKeyOperation.PrincipalTable);
            dropForeignKeyOperation.DependentTable = _GetNameWithReplacedSchema(dropForeignKeyOperation.DependentTable);
            base.Generate(dropForeignKeyOperation);
        }

        protected override void Generate(System.Data.Entity.Migrations.Model.DropIndexOperation dropIndexOperation)
        {
            dropIndexOperation.Table = _GetNameWithReplacedSchema(dropIndexOperation.Table);
            base.Generate(dropIndexOperation);
        }

        private string _GetNameWithReplacedSchema(string name)
        {
            string[] nameParts = name.Split('.');
            string newName;

            switch (nameParts.Length)
            {
                case 1:
                    newName = string.Format("{0}.{1}", _schema, nameParts[0]);
                    break;

                case 2:
                    newName = string.Format("{0}.{1}", _schema, nameParts[1]);
                    break;

                case 3:
                    newName = string.Format("{0}.{1}.{2}", _schema, nameParts[1], nameParts[2]);
                    break;

                default:
                    throw new NotSupportedException();
            }

            return newName;
        }
    }
}

وهذه هي الطريقة التي أستخدم بها SqlServerSchemaAwareMigrationSqlGenerator :

// Update TenantDataCtx
var tenantDataMigrationsConfiguration = new DbMigrationsConfiguration<TenantDataContext.TenantDataCtx>();
tenantDataMigrationsConfiguration.AutomaticMigrationsEnabled = false;
tenantDataMigrationsConfiguration.SetSqlGenerator("System.Data.SqlClient", new SqlServerSchemaAwareMigrationSqlGenerator(schemaName));
tenantDataMigrationsConfiguration.SetHistoryContextFactory("System.Data.SqlClient", (existingConnection, defaultSchema) => new HistoryContext(existingConnection, schemaName));
tenantDataMigrationsConfiguration.TargetDatabase = new System.Data.Entity.Infrastructure.DbConnectionInfo(connectionString, "System.Data.SqlClient");
tenantDataMigrationsConfiguration.MigrationsAssembly = typeof(TenantDataContext.TenantDataCtx).Assembly;
tenantDataMigrationsConfiguration.MigrationsNamespace = "TenantDataContext.Migrations.TenantData";

DbMigrator tenantDataCtxMigrator = new DbMigrator(tenantDataMigrationsConfiguration);
tenantDataCtxMigrator.Update();

مع تحيات من ألمانيا ،

توبياس




شكرا ل: ! أنت تدّخرني لسنوات

التعديل الخاص بي لـ Oracle DB تحت EF 6:

public class IntegrationDbContext : DbContext, IDbModelCacheKeyProvider
{
    private static readonly ILog __log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

    /// <summary>
    /// Factory method
    /// </summary>
    public static IntegrationDbContext Create(string connectionStringName)
    {
        return new IntegrationDbContext(connectionStringName, GetDBSchema(connectionStringName));
    }

    /// <summary>
    /// Constructor
    /// </summary>
    public IntegrationDbContext()
    {
        Database.SetInitializer<IntegrationDbContext>(null);
    }

    /// <summary>
    /// Constructor
    /// </summary>
    internal IntegrationDbContext(string connectionString, string schemaName)
        : base("name={0}".Fill(connectionString))
    {
        Database.SetInitializer<IntegrationDbContext>(null);
        SchemaName = schemaName;
    }

    /// <summary>
    /// DB schema name
    /// </summary>
    public string SchemaName { get; private set; }

    #region Tables
    /// <summary>
    /// Integration table "SYNC_BONUS_DISTRIBUTION"
    /// </summary>
    public virtual DbSet<SYNC_BONUS_DISTRIBUTION> SYNC_BONUS_DISTRIBUTION { get; set; }

    /// <summary>
    /// Integration table "SYNC_MESSAGE_DISTRIBUTION"
    /// </summary>
    public virtual DbSet<SYNC_MESSAGE_DISTRIBUTION> SYNC_MESSAGE_DISTRIBUTION { get; set; }

    /// <summary>
    /// Integration table "IMPORT_TEMPLATES"
    /// </summary>
    public virtual DbSet<IMPORT_TEMPLATE> IMPORT_TEMPLATES { get; set; }

    #endregion //Tables

    private static Dictionary<string, string> __schemaCache = new Dictionary<string, string>();
    private static object __schCacheLock = new object();
    /// <summary>
    /// Gets DB schema name from connection string, or default from config
    /// </summary>
    private static string GetDBSchema(string connectionStringName)
    {
        string result;
        if (!__schemaCache.TryGetValue(connectionStringName, out result))
        {
            lock (__schCacheLock)
            {
                if (!__schemaCache.TryGetValue(connectionStringName, out result))
                {
                    DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
                    builder.ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
                    result = builder.ContainsKey("User ID") ? builder["User ID"] as string : ConfigurationManager.AppSettings["DefaultIntegrationSchema"];
                    __schemaCache.Add(connectionStringName, result);
                }
            }
        }
        return result;
    }

    /// <summary>
    /// Context initialization
    /// </summary>
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        __log.DebugFormat("OnModelCreating for integration model in schema: {0}", SchemaName);
        if (SchemaName != null)
        {
            modelBuilder.HasDefaultSchema(SchemaName);
        }
        //### CLOB settings
        modelBuilder.Properties().Where(p => p.PropertyType == typeof(string) &&
                                             p.GetCustomAttributes(typeof(MaxLengthAttribute), false).Length == 0)
                                                .Configure(p => p.HasMaxLength(2000));

        base.OnModelCreating(modelBuilder);
    }

    /// <summary>
    /// Implementation of <see cref="IDbModelCacheKeyProvider.CacheKey"/> - thanks by this is 'OnModelCreating' calling for each specific schema.
    /// </summary>
    public string CacheKey
    {
        get { return SchemaName; }
    }
}



نهج لطيف للغاية وساعدني في الحصول على حل أكثر مباشرة إلى الأمام. يمكنك تجاوز اسم الأسلوب فقط ، يتم استخدامه في كل كاتب .... عذرا على إجابة جديدة ، ولكن لا يسمح لي بالتعليق ....

public class SqlServerSchemaAwareMigrationSqlGenerator:SqlServerMigrationSqlGenerator
{

    private string _schema;

    public accountMigrationSqlGenerator(string schema)
    {
        _schema = schema;
    }

    protected override string Name(string name)
    {

        int p = name.IndexOf('.');
        if(p>0)
        {
            name = name.Substring(p + 1);
        }

        return $"[{_schema}].[{name}]";

    }

}



TLDR. (شبه متعلق بعلة محرر EF في EF6 / VS2012U5) إذا قمت بإنشاء النموذج من DB ولا يمكنك رؤية الجدول m: m منسوب: حذف الجدولين المرتبطين -> حفظ .edmx -> توليد / إضافة من قاعدة البيانات - > احفظ.

بالنسبة لأولئك الذين جاؤوا إلى هنا يتساءلون عن كيفية الحصول على علاقة بين أطراف وأعمدة مميزة لعرضها في ملف EF .edmx (كما أنه لا يتم عرضه حاليًا ومعالجته كمجموعة من خصائص التنقل) ، وقمت بإنشاء هذه الفئات من جدول قاعدة البيانات الخاصة بك (أو قاعدة البيانات لأول مرة في لغة MS ، على ما أعتقد.)

قم بحذف الجدولين المذكورين (لأخذ مثال OP والعضو والتعليق) في ملف .edmx الخاص بك وقم بإضافتهما مرة أخرى من خلال "إنشاء نموذج من قاعدة البيانات". (أي لا تحاول السماح لـ Visual Studio بتحديثها - حذف ، حفظ ، إضافة ، حفظ)

سيؤدي بعد ذلك إلى إنشاء جدول ثالث بما يتم اقتراحه هنا.

ويكون ذلك مناسبًا في الحالات التي تتم فيها إضافة علاقة أطراف بأطراف في البداية ، ويتم تصميم السمات في DB لاحقًا.

لم يكن هذا واضحا على الفور من هذا الموضوع / غوغلينغ. لذا ضعها هنا فقط لأن هذا هو الرابط رقم 1 على Google الذي يبحث عن المشكلة ولكنه يأتي من جانب DB أولاً.





c# entity-framework