2

私は、マルチテナント (SaaS) サーバー/クライアント アーキテクチャを備えたアプリケーションを設計するという、かなり困難な仕事を任されてきました。私はサーバーの内部アーキテクチャについてしばらく考えてきましたが、これが私が持っているものです:

サーバーに WCF または MVC4 の WebAPI 実装を持たせたいと考えています。

サーバーは、MEF をオーケストレーターとして使用するプラグイン ベースのアーキテクチャを備えています。アプリケーションには、テナントの要件に基づいてビジネス ロジックを変更するいくつかの要件があるため、このようにすることを考えています。より具体的には、MEF でエクスポートするクラス/機能があり、そのクラスで、ホストされる WebAPI ルートのメタデータ属性を追加できます。明らかに、特定のケースではプラグインを拡張する必要があったり、他のプラグインによって排他的に使用されることさえあるかもしれません。その場合、プラグインにはルートがありません。

この時点でのサーバーに関する私の主な問題は、プラグインが必要とするすべてのものをプラグインが処理できるべきであるというプラグインアーキテクチャについて考えたことです。したがって、永続性に関しては、新しいプラグインが開発された場合、プラグインにコードを記述できるはずです。これは、プラグインのデータに永続性を提供するために DAL によって実行される可能性があります。マルチテナンシーとアクセスの制約はどこかから来る必要があるため、どのような状況でもプラグインが DAL を通過してデータベースにアクセスすることは望ましくありません。本質的に、DAL が EF で動作する場合、プラグインで永続性を定義する機能をオプションでどのように使用するかがわかりません。「CreatePersistentStorage(tenant)」のようなものをプラグインのインターフェースに入れることを考えましたが、テーブルの作成に関してプラグインを完全に制御できます。Orchard はこのようなことを行っていると思いますが、それははるかに堅牢な方法です。

プラグイン内で拡張できる DAL を実現する方法について助けが必要です。DAL は最終的な作成を制御する必要があるため、プラグインのデータ テーブルは適切なテナントの下に作成されます。

多分EFは行くべき道ではないでしょうか?あなたが考えていることについてのコメント/経験をいただければ幸いです。また、現時点で WCF WebAPI または ASP.NET WebAPI を使用しますか?

よろしく、 ラリ

4

1 に答える 1

0

さて、これが私が現在持っているものです、それはマルチテナンシーを提供します、それはプラグインを提供します-清潔さ/優雅さはそれほど多くありません。

public class Context : DbContext
{
    private static ConcurrentDictionary<string, DbCompiledModel> modelCache = 
               new ConcurrentDictionary<string, DbCompiledModel>();

    private Context(DbCompiledModel model) : base(model)
    {
    }

    public static Context Create(string tenantSchema)
    {
        var compiledModel = modelCache.GetOrAdd(
            tenantSchema,
            t =>
                {
                    var providerInfo = 
                        new DbProviderInfo("System.Data.SqlClient", "2008");
                    var modelBuilder = new DbModelBuilder();

                    modelBuilder.Conventions.Remove<IncludeMetadataConvention>();

                    foreach (var plugin in ServiceLocator
                        .Current
                        .GetAllInstances<IPlugin>().ToList())
                    {
                        plugin.SetupPersistence(modelBuilder,tenantSchema);
                    }

                    var model = modelBuilder.Build(providerInfo);
                    return model.Compile();
                });

        return new Context(compiledModel);
    }

    /// <summary>
    /// Creates the database and/or tables for a new tenant
    /// </summary>
    public static void ProvisionTenant(string tenantSchema)
    {
        using (var ctx = Create(tenantSchema))
        {
            if (!ctx.Database.Exists())
            {
                ctx.Database.Create();
            }
            else
            {
                try
                {
                    var createScript = ((IObjectContextAdapter)ctx)
                        .ObjectContext
                        .CreateDatabaseScript();
                    ctx.Database.ExecuteSqlCommand(createScript);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Tenant already exists.");        
                }
            }
        }
    }
}

プラグインは次のようになります。

[Export(typeof(IPlugin))]
public class Class2 : IPlugin
{

    public int Id { get; set; }
    public string Name { get; set; }
    public string Lol { get; set; }

    public void SetupPersistence(DbModelBuilder modelBuilder, string tenantSchema)
    {
        modelBuilder.Entity<Class2>().ToTable("Keke", tenantSchema);
        modelBuilder.Entity<Class2>().HasKey(i => i.Id);
    }
}

そして、これらをどのように使用するかを次に示します。

    Database.SetInitializer<Context>(null);

    Context.ProvisionTenant("personal");
    Context.ProvisionTenant("work");

    using (var db = Context.Create("personal"))
    {
        db.Set<Class2>().Add(new Class2 { Name = "Keke" });
        db.SaveChanges();
        foreach (var d in db.Set<Class2>())
        {
            Console.WriteLine(d.Name);
        }

        Console.Read();
    }

CommonServiceLocatorを使用してMEF参照を保持しています。

さて、これにはいくつかの問題があります。

  • プラグインはテーブル名を制御し、スキーマ名をテーブルに設定します-API側でこれをどのように制御するかわかりません。EF api全体を別のクラスにラップすることもできますが、1つの関数を希望どおりに機能させるだけでは、多くの問題が発生するようです。
  • これのポイントは、プラグイン拡張可能なデータベースレイヤーを持つことであり、拡張性のポイントは、将来発生する変更に備えることです。現在、私の実装では、処理する方法がないため、変更部分が不十分です。プラグイン内の移行。
  • それはひどいです。

移行を処理する方法、またはコンテキストからプラグインのテーブル名を明示的に定義する方法について、あらゆる入力を歓迎します。

于 2012-07-26T10:46:29.397 に答える