EF Core Azure Cosmos DB SağlayıcısıEF Core Azure Cosmos DB Provider

Not

Bu sağlayıcı EF Core 3.0'da yenidir.This provider is new in EF Core 3.0.

Bu veritabanı sağlayıcısı, Entity Framework Core'un Azure Cosmos DB ile kullanılmasına izin verir.This database provider allows Entity Framework Core to be used with Azure Cosmos DB. Sağlayıcı, Varlık Çerçeve Çekirdek Projesi'ninbir parçası olarak korunur.The provider is maintained as part of the Entity Framework Core Project.

Bu bölümü okumadan önce Azure Cosmos DB belgelerine alışmanız önemle tavsiye edilir.It is strongly recommended to familiarize yourself with the Azure Cosmos DB documentation before reading this section.

Not

Bu sağlayıcı yalnızca Azure Cosmos DB'nin SQL API'si ile çalışır.This provider only works with the SQL API of Azure Cosmos DB.

YüklemeInstall

Microsoft.EntityFrameworkCore.Cosmos NuGet paketiniyükleyin.Install the Microsoft.EntityFrameworkCore.Cosmos NuGet package.

dotnet add package Microsoft.EntityFrameworkCore.Cosmos

başlarkenGet started

İpucu

Bu makalenin örneğini GitHub'dagörüntüleyebilirsiniz.You can view this article's sample on GitHub.

Diğer sağlayıcılar için olduğu gibi ilk adım UseCosmosaramaktır:Like for other providers the first step is to call UseCosmos:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.UseCosmos(
            "https://localhost:8081",
            "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
            databaseName: "OrdersDB");

Uyarı

Uç nokta ve anahtar basitlik için burada kodlanır, ancak bir üretim uygulamasında bunlar güvenli bir şekilde depolanmalıdır.The endpoint and key are hardcoded here for simplicity, but in a production app these should be stored securely.

Bu Order örnekte, sahip olunan türe StreetAddressatıfta bulunan basit bir varlık yer almaktadır.In this example Order is a simple entity with a reference to the owned type StreetAddress.

public class Order
{
    public int Id { get; set; }
    public int? TrackingNumber { get; set; }
    public string PartitionKey { get; set; }
    public StreetAddress ShippingAddress { get; set; }
}
public class StreetAddress
{
    public string Street { get; set; }
    public string City { get; set; }
}

Veri kaydetme ve quering normal EF deseni izler:Saving and quering data follows the normal EF pattern:

using (var context = new OrderContext())
{
    await context.Database.EnsureDeletedAsync();
    await context.Database.EnsureCreatedAsync();

    context.Add(new Order
    {
        Id = 1,
        ShippingAddress = new StreetAddress { City = "London", Street = "221 B Baker St" },
        PartitionKey = "1"
    });

    await context.SaveChangesAsync();
}

using (var context = new OrderContext())
{
    var order = await context.Orders.FirstAsync();
    Console.WriteLine($"First order will ship to: {order.ShippingAddress.Street}, {order.ShippingAddress.City}");
    Console.WriteLine();
}

Önemli

Gerekli kapsayıcıları oluşturmak ve modelde varsa tohum verilerini eklemek için EnsureCreatedAsync'i aramak gereklidir.Calling EnsureCreatedAsync is necessary to create the required containers and insert the seed data if present in the model. Ancak, EnsureCreatedAsync performans sorunlarına neden olabileceğiiçin, normal çalışma sırasında değil, yalnızca dağıtım sırasında çağrılmalıdır.However EnsureCreatedAsync should only be called during deployment, not normal operation, as it may cause performance issues.

Cosmos'a özel model özelleştirmesiCosmos-specific model customization

Varsayılan olarak tüm varlık türleri, türetilmiş bağlamdan (bu"OrderContext" durumda) sonra adlandırılmış aynı kapsayıcıya eşlenir.By default all entity types are mapped to the same container, named after the derived context ("OrderContext" in this case). Varsayılan kapsayıcı adını değiştirmek için HasDefaultContainerkullanın:To change the default container name use HasDefaultContainer:

modelBuilder.HasDefaultContainer("Store");

Bir varlık türünü farklı bir kapsayıcıyla eşlemek için ToContainerkullanın:To map an entity type to a different container use ToContainer:

modelBuilder.Entity<Order>()
    .ToContainer("Orders");

Belirli bir öğenin EF Core'u temsil ettiği varlık türünü tanımlamak için, türetilmiş varlık türü olmasa bile ayrımcı bir değer ekler.To identify the entity type that a given item represent EF Core adds a discriminator value even if there are no derived entity types. Ayırıcının adı ve değeri değiştirilebilir.The name and value of the discriminator can be changed.

Başka bir varlık türü aynı kapta depolanmayacaksa, ayırıcı HasNoDiscriminator'uarayarak kaldırılabilir:If no other entity type will ever be stored in the same container the discriminator can be removed by calling HasNoDiscriminator:

modelBuilder.Entity<Order>()
    .HasNoDiscriminator();

Bölüm tuşlarıPartition keys

Varsayılan olarak EF Core, öğeleri eklerken "__partitionKey" bunun için herhangi bir değer sağlamadan bölüm anahtarı ayarlanmış kapsayıcılar oluşturur.By default EF Core will create containers with the partition key set to "__partitionKey" without supplying any value for it when inserting items. Ancak Azure Cosmos'un performans özelliklerinden tam olarak yararlanmak için özenle seçilmiş bir bölüm anahtarı kullanılmalıdır.But to fully leverage the performance capabilities of Azure Cosmos a carefully selected partition key should be used. Bu HasPartitionKeyarayarak yapılandırılabilir:It can be configured by calling HasPartitionKey:

modelBuilder.Entity<Order>()
    .HasPartitionKey(o => o.PartitionKey);

Not

Bölüm anahtar özelliği dize dönüştürüldüğüsürece herhangi bir tür olabilir.The partition key property can be of any type as long as it is converted to string.

Bir kez yapılandırılan bölüm anahtar özelliği her zaman null olmayan bir değere sahip olmalıdır.Once configured the partition key property should always have a non-null value. Sorgu verirken, tek bölümyapmak için bir koşul eklenebilir.When issuing a query a condition can be added to make it single-partition.

using (var context = new OrderContext())
{
    context.Add(new Order
    {
        Id = 2,
        ShippingAddress = new StreetAddress { City = "New York", Street = "11 Wall Street" },
        PartitionKey = "2"
    });

    await context.SaveChangesAsync();
}

using (var context = new OrderContext())
{
    var order = await context.Orders.Where(p => p.PartitionKey == "2").LastAsync();
    Console.Write("Last order will ship to: ");
    Console.WriteLine($"{order.ShippingAddress.Street}, {order.ShippingAddress.City}");
    Console.WriteLine();
}

Gömülü varlıklarEmbedded entities

Çünkü Cosmos'a ait varlıklar sahibiyle aynı öğeye gömülür.For Cosmos owned entities are embedded in the same item as the owner. Bir özellik adını değiştirmek için ToJsonPropertykullanın:To change a property name use ToJsonProperty:

modelBuilder.Entity<Order>().OwnsOne(
    o => o.ShippingAddress,
    sa =>
    {
        sa.ToJsonProperty("Address");
        sa.Property(p => p.Street).ToJsonProperty("ShipsToStreet");
        sa.Property(p => p.City).ToJsonProperty("ShipsToCity");
    });

Bu yapılandırma ile yukarıdaki örnekten gelen sipariş aşağıdaki gibi depolanır:With this configuration the order from the example above is stored like this:

{
    "Id": 1,
    "PartitionKey": "1",
    "TrackingNumber": null,
    "id": "1",
    "Address": {
        "ShipsToCity": "London",
        "ShipsToStreet": "221 B Baker St"
    },
    "_rid": "6QEKAM+BOOABAAAAAAAAAA==",
    "_self": "dbs/6QEKAA==/colls/6QEKAM+BOOA=/docs/6QEKAM+BOOABAAAAAAAAAA==/",
    "_etag": "\"00000000-0000-0000-683c-692e763901d5\"",
    "_attachments": "attachments/",
    "_ts": 1568163674
}

Sahip olunan varlıkların koleksiyonları da gömülür.Collections of owned entities are embedded as well. Bir sonraki örnek için Distributor bir koleksiyon ile StreetAddresssınıf kullanacağız:For the next example we'll use the Distributor class with a collection of StreetAddress:

public class Distributor
{
    public int Id { get; set; }
    public ICollection<StreetAddress> ShippingCenters { get; set; }
}

Sahip olunan varlıkların depolanacak açık anahtar değerleri sağlaması gerekmez:The owned entities don't need to provide explicit key values to be stored:

var distributor = new Distributor
{
    Id = 1,
    ShippingCenters = new HashSet<StreetAddress> {
            new StreetAddress { City = "Phoenix", Street = "500 S 48th Street" },
            new StreetAddress { City = "Anaheim", Street = "5650 Dolly Ave" }
        }
};

using (var context = new OrderContext())
{
    context.Add(distributor);

    await context.SaveChangesAsync();
}

Bu şekilde devam edeceklerdir:They will be persisted in this way:

{
    "Id": 1,
    "Discriminator": "Distributor",
    "id": "Distributor|1",
    "ShippingCenters": [
        {
            "City": "Phoenix",
            "Street": "500 S 48th Street"
        },
        {
            "City": "Anaheim",
            "Street": "5650 Dolly Ave"
        }
    ],
    "_rid": "6QEKANzISj0BAAAAAAAAAA==",
    "_self": "dbs/6QEKAA==/colls/6QEKANzISj0=/docs/6QEKANzISj0BAAAAAAAAAA==/",
    "_etag": "\"00000000-0000-0000-683c-7b2b439701d5\"",
    "_attachments": "attachments/",
    "_ts": 1568163705
}

Dahili OLARAK EF Core'un izlenen tüm varlıklar için her zaman benzersiz anahtar değerlere sahip olması gerekir.Internally EF Core always needs to have unique key values for all tracked entities. Sahip olunan türlerin koleksiyonları için varsayılan olarak oluşturulan birincil anahtar, sahibini int gösteren yabancı anahtar özellikleri ve JSON dizisinde dizideki diziye karşılık gelen bir özellikten oluşur.The primary key created by default for collections of owned types consists of the foreign key properties pointing to the owner and an int property corresponding to the index in the JSON array. Bu değerler giriş API almak için kullanılabilir:To retrieve these values entry API could be used:

using (var context = new OrderContext())
{
    var firstDistributor = await context.Distributors.FirstAsync();
    Console.WriteLine($"Number of shipping centers: {firstDistributor.ShippingCenters.Count}");

    var addressEntry = context.Entry(firstDistributor.ShippingCenters.First());
    var addressPKProperties = addressEntry.Metadata.FindPrimaryKey().Properties;

    Console.WriteLine($"First shipping center PK: ({addressEntry.Property(addressPKProperties[0].Name).CurrentValue}, {addressEntry.Property(addressPKProperties[1].Name).CurrentValue})");
    Console.WriteLine();
}

İpucu

Gerektiğinde sahip olunan varlık türleri için varsayılan birincil anahtar değiştirilebilir, ancak daha sonra anahtar değerleri açıkça sağlanmalıdır.When necessary the default primary key for the owned entity types can be changed, but then key values should be provided explicitly.

Bağlantısı kesilen varlıklarla çalışmaWorking with disconnected entities

Her öğenin verilen id bölüm anahtarı için benzersiz bir değere sahip olması gerekir.Every item needs to have an id value that is unique for the given partition key. Varsayılan olarak EF Core, ayırıcıyı ve birincil anahtar değerlerini bir sınırlayıcı olarak '|' kullanarak bir değeri biraraya gelir.By default EF Core generates the value by concatenating the discriminator and the primary key values, using '|' as a delimiter. Anahtar değerler yalnızca bir varlık Added duruma girdiğinde oluşturulur.The key values are only generated when an entity enters the Added state. Değeri depolamak için .NET türünde bir id özelliği yoksa, varlıkları takarken sorun yaratabilir.This might pose a problem when attaching entities if they don't have an id property on the .NET type to store the value.

Bu sınırlamayı id aşmak için değer el ile oluşturulabilir ve ayarlanabilir veya varlığı önce eklenen olarak işaretleyebilir, sonra istenilen duruma göre değiştirebiliriz:To work around this limitation one could create and set the id value manually or mark the entity as added first, then changing it to the desired state:

using (var context = new OrderContext())
{
    var distributorEntry = context.Add(distributor);
    distributorEntry.State = EntityState.Unchanged;

    distributor.ShippingCenters.Remove(distributor.ShippingCenters.Last());

    await context.SaveChangesAsync();
}

using (var context = new OrderContext())
{
    var firstDistributor = await context.Distributors.FirstAsync();
    Console.WriteLine($"Number of shipping centers is now: {firstDistributor.ShippingCenters.Count}");

    var distributorEntry = context.Entry(firstDistributor);
    var idProperty = distributorEntry.Property<string>("id");
    Console.WriteLine($"The distributor 'id' is: {idProperty.CurrentValue}");
}

Bu, ortaya çıkan JSON:This is the resulting JSON:

{
    "Id": 1,
    "Discriminator": "Distributor",
    "id": "Distributor|1",
    "ShippingCenters": [
        {
            "City": "Phoenix",
            "Street": "500 S 48th Street"
        }
    ],
    "_rid": "JBwtAN8oNYEBAAAAAAAAAA==",
    "_self": "dbs/JBwtAA==/colls/JBwtAN8oNYE=/docs/JBwtAN8oNYEBAAAAAAAAAA==/",
    "_etag": "\"00000000-0000-0000-9377-d7a1ae7c01d5\"",
    "_attachments": "attachments/",
    "_ts": 1572917100
}