Aracılığıyla paylaş


Çok katmanlı mimari

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.

Uyarı

Bu makalede, kullanıcının kimliğinin doğrulanması gerekmeyen bir yerel veritabanı kullanılır. Üretim uygulamaları kullanılabilir en güvenli kimlik doğrulama akışını kullanmalıdır. Dağıtılan test ve üretim uygulamaları için kimlik doğrulaması hakkında daha fazla bilgi için bkz . Güvenli kimlik doğrulama akışları.

Ö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ı için sütun mu? Kiracı başına şema mı? Birden çok Veritabanı mı? EF Core Desteği
Ayırt Edici (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 Hayır 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 Varsayılan olarak, fabrika bir tekil nesne olduğundan, uygulamanın tüm kullanıcıları için yalnızca bir tane mevcuttur. Genellikle sorun olmaz çünkü fabrika paylaşılsa da, tek tek DbContext örnekler paylaşılmamaktadır.

Çok kiracılı kullanımda, bağlantı dizesi kullanıcıya göre 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 meydana gelmez çünkü singleton kullanıcıya özgü şekilde sınırlıdı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ü sınırlıdır ve her kullanıcı oturumu için yeni bir örnek oluşturulur.

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

Kullanıcının geçerli kiracısını ayarlayan basit bir ITenantService hizmeti oluşturmak olası bir çözümdü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;
    }
}

DbContext daha sonra çoklu kiracılığı 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;

OnModelCreating yöntemi, sorgu filtresini belirtmek için geçersiz kılınmıştır.

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ıya bağımlı olmasını sağlar.

Not

Bağımlılıklar her zaman singleton nesneye 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ı tenant1 ve tenant2 öğelerini tablo şemalarını kullanarak işleyebilir.

  • 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 kullanılarak uygulanır. Uygulama başlangıcında, hizmet sağlayıcısı çözümlenerek ve bu hizmet sağlayıcısı kullanılarak bağlantı dizesi oluşturulabilir. 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ırma her ikisi de DbContext içerisine enjekte edilir.

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

Kiracı, ardından OnConfiguring içindeki bağlantı dizesini aramak 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 Scoped düzeyinde önbelleğe alınır. 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ıya özgü bağımlılıkları kabul etmiyorsa, yine de Singleton varsayılan değer anlamlıdır.

Performans notları

EF Core, DbContext örneklerinin mümkün olduğunca az ek yükle ve hızlı bir şekilde oluşturulabilmesi için tasarlanmıştır. Bu nedenle, işlem başına yeni bir DbContext oluşturmak genellikle iyi olacaktı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.