Aracılığıyla paylaş


Çok kiracılı

Birçok iş kolu uygulaması birden çok müşteriyle çalışacak şekilde tasarlanmıştır. Müşteri verilerinin "sızdırılması" veya diğer müşteriler ve olası rakipler tarafından görülmemesi için verilerin güvenliğini sağlamak önemlidir. Her müşteri kendi veri kümesine sahip bir uygulama kiracısı olarak kabul edildiğinden, bu uygulamalar "çok kiracılı" olarak sınıflandırılır.

Önemli

Bu belgede "olduğu gibi" örnekler ve çözümler sağlanır. Bunlar "en iyi uygulamalar" değil, sizin dikkate alınması gereken "çalışma uygulamaları" olarak tasarlanmıştır.

Çoklu kiracıyı destekleme

Uygulamalarda çok kiracılılık uygulamaya yönelik birçok yaklaşım vardır. Yaygın yaklaşımlardan biri (bazen bir gereksinimdir), her müşteri için verileri ayrı bir veritabanında tutmaktır. Şema aynıdır ancak veriler müşteriye özgüdür. Bir diğer yaklaşım da mevcut veritabanındaki verileri müşteriye göre bölümlendirmektir. Bu, bir tablodaki bir sütun kullanılarak veya her kiracı için şema içeren birden çok şemada bir tablo bulunarak yapılabilir.

Yaklaşım Kiracı sütunu mu? Kiracı başına şema mı? Birden çok Veritabanı mı? EF Core Desteği
Ayrımcı (sütun) Evet Hayır Hayır Genel sorgu filtresi
Kiracı başına veritabanı Hayır Hayır Evet Yapılandırma
Kiracı başına şema No. Evet Hayır Desteklenmez

Kiracı başına veritabanı yaklaşımı için doğru veritabanına geçiş yapmak, doğru bağlantı dizesi sağlamak kadar basittir. Veriler tek bir veritabanında depolandığında, genel sorgu filtresi , kiracı kimliği sütununa göre satırları otomatik olarak filtrelemek için kullanılabilir ve geliştiricilerin diğer müşterilerin verilerine erişebilecek kodu yanlışlıkla yazmamasını sağlar.

Bu örnekler konsol, WPF, WinForms ve ASP.NET Core uygulamaları dahil olmak üzere çoğu uygulama modelinde sorunsuz çalışmalıdır. Blazor Server uygulamaları özel bir değerlendirme gerektirir.

Blazor Server uygulamaları ve fabrikanın ömrü

Blazor uygulamalarında Entity Framework Core'u kullanmak için önerilen desen, DbContextFactory'yi kaydetmek ve ardından her işlemin yeni bir örneğini oluşturmak için bunu çağırmaktırDbContext. Varsayılan olarak, fabrika tek bir kopya olduğundan uygulamanın tüm kullanıcıları için yalnızca bir kopya vardır. Fabrika paylaşılsa da tek tek DbContext örnekler paylaşılmadığından bu genellikle sorun olmaz.

Ancak çok kiracılı kullanım için bağlantı dizesi kullanıcı başına değişebilir. Fabrika yapılandırmayı aynı yaşam süresiyle önbelleğe alır, bu da tüm kullanıcıların aynı yapılandırmayı paylaşması gerektiği anlamına gelir. Bu nedenle, yaşam süresi olarak Scopeddeğiştirilmelidir.

Bu sorun Blazor WebAssembly uygulamalarında oluşmaz çünkü singleton kullanıcı kapsamına alınır. Öte yandan Blazor Server uygulamaları benzersiz bir zorluk sunar. Uygulama bir web uygulaması olsa da SignalR kullanılarak gerçek zamanlı iletişim tarafından "canlı tutulur". Kullanıcı başına bir oturum oluşturulur ve ilk isteğin ötesinde sürer. Yeni ayarlara izin vermek için kullanıcı başına yeni bir fabrika sağlanmalıdır. Bu özel fabrikanın kullanım ömrü kapsamlıdır ve kullanıcı oturumu başına yeni bir örnek oluşturulur.

Örnek bir çözüm (tek veritabanı)

Olası bir çözüm, kullanıcının geçerli kiracısını ayarlamayı işleyen basit ITenantService bir hizmet oluşturmaktır. Kiracı değiştiğinde kodun bildirilmesi için geri çağırmalar sağlar. Uygulama (netlik için geri çağırmalar atlanmış olarak) şöyle görünebilir:

namespace Common
{
    public interface ITenantService
    {
        string Tenant { get; }

        void SetTenant(string tenant);

        string[] GetTenants();

        event TenantChangedEventHandler OnTenantChanged;
    }
}

daha DbContext sonra çoklu kiracıyı yönetebilir. Yaklaşım, veritabanı stratejinize bağlıdır. Tüm kiracıları tek bir veritabanında depoluyorsanız, büyük olasılıkla bir sorgu filtresi kullanacaksınız. ITenantService, bağımlılık ekleme yoluyla oluşturucuya geçirilir ve kiracı tanımlayıcısını çözümlemek ve depolamak için kullanılır.

public ContactContext(
    DbContextOptions<ContactContext> opts,
    ITenantService service)
    : base(opts) => _tenant = service.Tenant;

Sorgu OnModelCreating filtresini belirtmek için yöntemi geçersiz kılınmış:

protected override void OnModelCreating(ModelBuilder modelBuilder)
    => modelBuilder.Entity<MultitenantContact>()
        .HasQueryFilter(mt => mt.Tenant == _tenant);

Bu, her sorgunun her istekte kiracıya filtrelendiğinden emin olmasını sağlar. Genel filtre otomatik olarak uygulanacağından uygulama kodunda filtreleme yapmanız gerekmez.

Kiracı sağlayıcısı ve DbContextFactory örnek olarak Sqlite kullanılarak uygulama başlangıcında aşağıdaki gibi yapılandırılır:

builder.Services.AddDbContextFactory<ContactContext>(
    opt => opt.UseSqlite("Data Source=singledb.sqlite"), ServiceLifetime.Scoped);

Hizmet ömrünün ile ServiceLifetime.Scopedyapılandırıldığına dikkat edin. Bu, kiracı sağlayıcısına bağımlılık almasını sağlar.

Dekont

Bağımlılıklar her zaman tekliye doğru akmalıdır. Bu, hizmetin başka bir ScopedScoped hizmete veya Singleton hizmete bağımlı olabileceği, ancak Singleton hizmetin yalnızca diğer Singleton hizmetlere bağlı olabileceği anlamına gelir: Transient => Scoped => Singleton.

Birden çok şema

Uyarı

Bu senaryo EF Core tarafından doğrudan desteklenmez ve önerilen bir çözüm değildir.

Farklı bir yaklaşımda, aynı veritabanı tablo şemalarını kullanarak ve tenant2 işleyebilirtenant1.

  • Kiracı1 - tenant1.CustomerData
  • Kiracı2 - tenant2.CustomerData

Geçişlerle veritabanı güncelleştirmelerini işlemek için EF Core kullanmıyorsanız ve zaten çok şemalı tablolarınız varsa, içindeki şemayı DbContextOnModelCreating şöyle geçersiz kılabilirsiniz (tablo CustomerData şeması kiracıya ayarlanır):

protected override void OnModelCreating(ModelBuilder modelBuilder) =>
    modelBuilder.Entity<CustomerData>().ToTable(nameof(CustomerData), tenant);

Birden çok veritabanı ve bağlantı dizesi

Birden çok veritabanı sürümü, her kiracı için farklı bir bağlantı dizesi geçirilerek uygulanır. Bu, başlangıçta hizmet sağlayıcısı çözümlenerek ve bağlantı dizesi oluşturmak için kullanılarak yapılandırılabilir. Yapılandırma dosyasına kiracıya göre bağlantı dizesi bölümü eklenirappsettings.json.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "TenantA": "Data Source=tenantacontacts.sqlite",
    "TenantB": "Data Source=tenantbcontacts.sqlite"
  },
  "AllowedHosts": "*"
}

Hizmet ve yapılandırmanın her ikisi de içine DbContexteklenir:

public ContactContext(
    DbContextOptions<ContactContext> opts,
    IConfiguration config,
    ITenantService service)
    : base(opts)
{
    _tenantService = service;
    _configuration = config;
}

Kiracı daha sonra içindeki bağlantı dizesi OnConfiguringaramak için kullanılır:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    var tenant = _tenantService.Tenant;
    var connectionStr = _configuration.GetConnectionString(tenant);
    optionsBuilder.UseSqlite(connectionStr);
}

Bu, kullanıcı aynı oturum sırasında kiracıları değiştiremiyorsa çoğu senaryoda düzgün çalışır.

Kiracılar arasında geçiş yapma

Birden çok veritabanı için önceki yapılandırmada, seçenekler düzeyinde önbelleğe alınır Scoped . Bu, kullanıcı kiracıyı değiştirirse seçeneklerin yeniden değerlendirilmediği ve dolayısıyla kiracı değişikliğinin sorgulara yansıtılmadığı anlamına gelir.

Kiracının değişebileceği durumlarda bunun kolay çözümü, yaşam süresini Transient. Bu olarak ayarlamaktır. Bu, kiracının her istendiğinde DbContext bağlantı dizesi ile birlikte yeniden değerlendirilmesini sağlar. Kullanıcı kiracıları istedikleri sıklıkta değiştirebilir. Aşağıdaki tablo, fabrikanız için en anlamlı ömrü seçmenize yardımcı olur.

Senaryo Tek veritabanı Birden çok veritabanı
Kullanıcı tek bir kiracıda kalır Scoped Scoped
Kullanıcı kiracıları değiştirebilir Scoped Transient

Veritabanınız kullanıcı kapsamlı bağımlılıkları almazsa varsayılan Singleton değeri hala anlamlıdır.

Performans notları

EF Core, örneklerin DbContext mümkün olduğunca az ek yükle hızlı bir şekilde örnek oluşturulabilmesi için tasarlanmıştır. Bu nedenle, işlem başına yeni DbContext bir oluşturma işlemi genellikle iyi olmalıdır. Bu yaklaşım uygulamanızın performansını etkiliyorsa DbContext havuzu kullanmayı göz önünde bulundurun.

Sonuç

Bu, EF Core uygulamalarında çok kiracılılık uygulamaya yönelik çalışma kılavuzudur. Başka örnekleriniz veya senaryolarınız varsa veya geri bildirim sağlamak istiyorsanız lütfen bir sorun açın ve bu belgeye başvurun.