diff --git a/entity-framework/core/miscellaneous/connection-strings.md b/entity-framework/core/miscellaneous/connection-strings.md index 5847636e60..a86ee97c46 100644 --- a/entity-framework/core/miscellaneous/connection-strings.md +++ b/entity-framework/core/miscellaneous/connection-strings.md @@ -11,7 +11,7 @@ Most database providers require some form of connection string to connect to the ## .NET Framework Applications -.NET Framework applications, such as WinForms, WPF, Console, and ASP.NET 4, have a tried and tested connection string pattern. The connection string should be added to your application's App.config file (Web.config if you are using ASP.NET). If your connection string contains sensitive information, such as username and password, you can protect the contents of the configuration file using [Protected Configuration](https://docs.microsoft.com/dotnet/framework/data/adonet/connection-strings-and-configuration-files#encrypting-configuration-file-sections-using-protected-configuration). +.NET Framework applications, such as WinForms, WPF, Console, and ASP.NET 4, have a tried and tested connection string pattern. The connection string should be added to your application's App.config file (Web.config if you are using ASP.NET). If your connection string contains sensitive information, such as username and password, you can protect the contents of the configuration file using the [Secret Manager tool](https://docs.microsoft.com/aspnet/core/security/app-secrets#secret-manager). ``` xml diff --git a/entity-framework/core/modeling/table-splitting.md b/entity-framework/core/modeling/table-splitting.md index 5e903bcb74..7b02722311 100644 --- a/entity-framework/core/modeling/table-splitting.md +++ b/entity-framework/core/modeling/table-splitting.md @@ -29,7 +29,8 @@ In addition to the required configuration we call `HasBaseType((string)null)` to [!code-csharp[TableSplittingConfiguration](../../../samples/core/Modeling/TableSplitting/TableSplittingContext.cs?name=TableSplitting&highlight=3)] -See the [full sample project](https://github.com/aspnet/EntityFramework.Docs/tree/master/samples/core/Modeling/TableSplitting) for more context. +> [!TIP] +> See the [full sample project](https://github.com/aspnet/EntityFramework.Docs/tree/master/samples/core/Modeling/TableSplitting) for more context. ## Usage diff --git a/entity-framework/core/providers/cosmos/index.md b/entity-framework/core/providers/cosmos/index.md new file mode 100644 index 0000000000..2734e018d2 --- /dev/null +++ b/entity-framework/core/providers/cosmos/index.md @@ -0,0 +1,157 @@ +--- +title: Azure Cosmos DB Provider - EF Core +author: AndriySvyryd +ms.author: ansvyryd +ms.date: 09/12/2019 +ms.assetid: 28264681-4486-4891-888c-be5e4ade24f1 +uid: core/providers/cosmos/index +--- +# EF Core Azure Cosmos DB Provider + +>[!NOTE] +> This provider is new in EF Core 3.0. + +This database provider allows Entity Framework Core to be used with Azure Cosmos DB. The provider is maintained as part of the [Entity Framework Core Project](https://github.com/aspnet/EntityFrameworkCore). + +It is strongly recommended to familiarize yourself with the [Azure Cosmos DB documentation](https://docs.microsoft.com/en-us/azure/cosmos-db/introduction) before reading this section. + +## Install + +Install the [Microsoft.EntityFrameworkCore.Cosmos NuGet package](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Cosmos/). + +``` powershell +Install-Package Microsoft.EntityFrameworkCore.Cosmos +``` + +## Get Started + +> [!TIP] +> You can view this article's [sample on GitHub](https://github.com/aspnet/EntityFramework.Docs/tree/master/samples/core/Cosmos). + +Like for other providers the first step is to call `UseCosmos`: +[!code-csharp[Configuration](../../../../samples/core/Cosmos/ModelBuilding/OrderContext.cs?name=Configuration)] + +> [!WARNING] +> The endpoint and key are hardcoded here for simplicity, but in a production app these should be [stored securily](https://docs.microsoft.com/aspnet/core/security/app-secrets#secret-manager) + +In this example `Order` is a simple entity with a reference to the [owned type](../../modeling/owned-entities.md) `StreetAddress`. + +[!code-csharp[Order](../../../../samples/core/Cosmos/ModelBuilding/Order.cs?name=Order)] + +[!code-csharp[StreetAddress](../../../../samples/core/Cosmos/ModelBuilding/StreetAddress.cs?name=StreetAddress)] + +Saving and quering data follows the normal EF pattern: +[!code-csharp[HelloCosmos](../../../../samples/core/Cosmos/ModelBuilding/Sample.cs?name=HelloCosmos)] + +> [!IMPORTANT] +> Calling `EnsureCreated` is necessary to create the required collections and insert the [seed data](../../modeling/data-seeding.md) if present in the model. However `EnsureCreated` should only be called during deployment, not normal operation, as it may cause performance issues. + +## Cosmos-specific Model Customization + +By default all entity types are mapped to the same container, named after the derived context (`"OrderContext"` in this case). To change the default container name use `HasDefaultContainer`: + +[!code-csharp[DefaultContainer](../../../../samples/core/Cosmos/ModelBuilding/OrderContext.cs?name=DefaultContainer)] + +To map an entity type to a different container use `ToContainer`: + +[!code-csharp[Container](../../../../samples/core/Cosmos/ModelBuilding/OrderContext.cs?name=Container)] + +To identify the entity type that a given item represent EF Core adds a discriminator value even if there are no derived entity types. The name and value of the discriminator [can be changed](../../modeling/inheritance.md). + +## Embedded Entities + +For Cosmos owned entities are embedded in the same item as the owner. To change a property name use `ToJsonProperty`: + +[!code-csharp[PropertyNames](../../../../samples/core/Cosmos/ModelBuilding/OrderContext.cs?name=PropertyNames)] + +With this configuration the order from the example above is stored like this: + +``` json +{ + "Id": 1, + "Discriminator": "Order", + "TrackingNumber": null, + "id": "Order|1", + "Address": { + "ShipsToCity": "London", + "Discriminator": "StreetAddress", + "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 +} +``` + +Collections of owned entities are embedded as well. For the next example we'll use the `Distributor` class with a collection of `StreetAddress`: + +[!code-csharp[Distributor](../../../../samples/core/Cosmos/ModelBuilding/Distributor.cs?name=Distributor)] + +The owned entities don't need to provide explicit key values to be stored: + +[!code-csharp[OwnedCollection](../../../../samples/core/Cosmos/ModelBuilding/Sample.cs?name=OwnedCollection)] + +They will be persisted in this way: + +``` json +{ + "Id": 1, + "Discriminator": "Distributor", + "id": "Distributor|1", + "ShippingCenters": [ + { + "City": "Phoenix", + "Discriminator": "StreetAddress", + "Street": "500 S 48th Street" + }, + { + "City": "Anaheim", + "Discriminator": "StreetAddress", + "Street": "5650 Dolly Ave" + } + ], + "_rid": "6QEKANzISj0BAAAAAAAAAA==", + "_self": "dbs/6QEKAA==/colls/6QEKANzISj0=/docs/6QEKANzISj0BAAAAAAAAAA==/", + "_etag": "\"00000000-0000-0000-683c-7b2b439701d5\"", + "_attachments": "attachments/", + "_ts": 1568163705 +} +``` + +Internally EF Core always needs to have unique key values for all tracked entities. 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. To retrieve these values entry API could be used: + +[!code-csharp[ImpliedProperties](../../../../samples/core/Cosmos/ModelBuilding/Sample.cs?name=ImpliedProperties)] + +> [!TIP] +> When necessary the default primary key for the owned entity types can be changed, but then key values should be provided explicitly. + +## Working with Disconnected Entities + +Every item needs to have an `id` value that is unique for the given partition key. By default EF Core generates the value by concatenating the discriminator and the primary key values, using '|' as a delimiter. The key values are only generated when an entity enters the `Added` state. This might pose a problem when [attaching entities](../../saving/disconnected-entities.md) if they don't have an `id` property on the CLR type to store the value. + +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: + +[!code-csharp[Attach](../../../../samples/core/Cosmos/ModelBuilding/Sample.cs?highlight=4&name=Attach)] + +This is the resulting JSON: + +``` json +{ + "Id": 1, + "Discriminator": "Order", + "TrackingNumber": null, + "id": "Order|1", + "Address": { + "ShipsToCity": "London", + "Discriminator": "StreetAddress", + "ShipsToStreet": "3 Abbey Road" + }, + "_rid": "6QEKAM+BOOABAAAAAAAAAA==", + "_self": "dbs/6QEKAA==/colls/6QEKAM+BOOA=/docs/6QEKAM+BOOABAAAAAAAAAA==/", + "_etag": "\"00000000-0000-0000-683c-8f7ac48f01d5\"", + "_attachments": "attachments/", + "_ts": 1568163739 +} +``` \ No newline at end of file diff --git a/entity-framework/core/providers/cosmos/limitations.md b/entity-framework/core/providers/cosmos/limitations.md new file mode 100644 index 0000000000..f4bef86097 --- /dev/null +++ b/entity-framework/core/providers/cosmos/limitations.md @@ -0,0 +1,32 @@ +--- +title: Azure Cosmos DB Provider - Limitations - EF Core +author: AndriySvyryd +ms.author: ansvyryd +ms.date: 09/12/2019 +ms.assetid: 9d02a2cd-484e-4687-b8a8-3748ba46dbc9 +uid: core/providers/cosmos/limitations +--- +# EF Core Azure Cosmos DB Provider Limitations + +The Cosmos provider has a number of limitations. Many of these limitations are a result of limitations in the underlying Cosmos database engine and are not specific to EF. But most simply [haven't been implemented yet](https://github.com/aspnet/EntityFrameworkCore/issues?page=1&q=is%3Aissue+is%3Aopen+Cosmos+in%3Atitle+label%3Atype-enhancement+sort%3Areactions-%2B1-desc). + +## Temporary limitations + +- Even if there is only one entity type without inheritance mapped to a container it still has a discriminator property. +- Entity types with partition keys don't work correctly in some scenarios +- `Include` calls are not supported +- `Join` calls are not supported + +## Azure Cosmos DB SDK limitations + +- Only async methods are provided + +> [!WARNING] +> Since there are no sync versions of the low level methods EF Core relies on, the corresponding functionality is currently implemented by calling `.Wait()` on the returned `Task`. This means that using methods like `SaveChanges`, or `ToList` instead of their async counterparts could lead to a deadlock in your application + +## Azure Cosmos DB limitations + +You can see the full overview of [Azure Cosmos DB supported features](https://docs.microsoft.com/en-us/azure/cosmos-db/modeling-data), these are the most notable differences compared to a relational database: + +- Client-initiated transactions are not supported +- Some cross-partition queries are either not supported or much slower depending on the operators involved \ No newline at end of file diff --git a/entity-framework/core/providers/cosmos/unstructured-data.md b/entity-framework/core/providers/cosmos/unstructured-data.md new file mode 100644 index 0000000000..29fadebd3b --- /dev/null +++ b/entity-framework/core/providers/cosmos/unstructured-data.md @@ -0,0 +1,60 @@ +--- +title: Azure Cosmos DB Provider - Working with Unstructured Data - EF Core +author: AndriySvyryd +ms.author: ansvyryd +ms.date: 09/12/2019 +ms.assetid: b47d41b6-984f-419a-ab10-2ed3b95e3919 +uid: core/providers/cosmos/unstructured-data +--- +# Working with Unstructured Data in EF Core Azure Cosmos DB Provider + +EF Core was designed to make it easy to work with data that follows a schema defined in the model. However one of the strengths of Azure Cosmos DB is the flexibility in the shape of the data stored. + +## Accessing the raw JSON + +It is possible to access the properties that are not tracked by EF Core through a special property in [shadow-state](../../modeling/shadow-properties.md) named `"__jObject"` that contains a `JObject` representing the data recieved from the store and data that will be stored: + +[!code-csharp[Unmapped](../../../../samples/core/Cosmos/UnstructuredData/Sample.cs?highlight=21-23&name=Unmapped)] + +``` json +{ + "Id": 1, + "Discriminator": "Order", + "TrackingNumber": null, + "id": "Order|1", + "Address": { + "ShipsToCity": "London", + "Discriminator": "StreetAddress", + "ShipsToStreet": "221 B Baker St" + }, + "_rid": "eLMaAK8TzkIBAAAAAAAAAA==", + "_self": "dbs/eLMaAA==/colls/eLMaAK8TzkI=/docs/eLMaAK8TzkIBAAAAAAAAAA==/", + "_etag": "\"00000000-0000-0000-683e-0a12bf8d01d5\"", + "_attachments": "attachments/", + "BillingAddress": "Clarence House", + "_ts": 1568164374 +} +``` + +> [!WARNING] +> The `"__jObject"` property is part of the EF Core infrastructure and should only be used as a last resort as it is likely to have different behavior in future releases. + +> [!NOTE] +> Changes to the entity will override the values stored in `"__jObject"` during `SaveChanges`. + +## Using CosmosClient + +To decouple completely from EF Core get the `CosmosClient` object that is [part of the Azure Cosmos DB SDK](https://docs.microsoft.com/en-us/azure/cosmos-db/sql-api-get-started) from `DbContext`: + +[!code-csharp[CosmosClient](../../../../samples/core/Cosmos/UnstructuredData/Sample.cs?highlight=3&name=CosmosClient)] + +## Missing property values + +In the previous example we removed the `"TrackingNumber"` property from the order. Because of how indexing works in Cosmos DB, queries that reference the missing property somewhere else than in the projection could return unexpected results. For example: + +[!code-csharp[MissingProperties](../../../../samples/core/Cosmos/UnstructuredData/Sample.cs?name=MissingProperties)] + +The sorted query actually returns no results. This means that one should take care to always populate properties mapped by EF Core when working with the store directly. + +> [!NOTE] +> This behavior might change in future versions of Cosmos. For instance, currently if the indexing policy defines the composite index {Id/? ASC, TrackingNumber/? ASC)}, then a query the has 'ORDER BY c.Id ASC, c.Discriminator ASC' __would__ return items that are missing the `"TrackingNumber"` property. \ No newline at end of file diff --git a/entity-framework/core/providers/index.md b/entity-framework/core/providers/index.md index 405b7c0c37..f03ba5a412 100644 --- a/entity-framework/core/providers/index.md +++ b/entity-framework/core/providers/index.md @@ -19,7 +19,7 @@ Entity Framework Core can access many different databases through plug-in librar | [Microsoft.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.SqlServer) | SQL Server 2008 onwards | [EF Core Project](https://github.com/aspnet/EntityFrameworkCore/) (Microsoft) | | [docs](xref:core/providers/sql-server/index) | | [Microsoft.EntityFrameworkCore.Sqlite](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite) | SQLite 3.7 onwards | [EF Core Project](https://github.com/aspnet/EntityFrameworkCore/) (Microsoft) | | [docs](xref:core/providers/sqlite/index) | | [Microsoft.EntityFrameworkCore.InMemory](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.InMemory) | EF Core in-memory database | [EF Core Project](https://github.com/aspnet/EntityFrameworkCore/) (Microsoft) | For testing only | [docs](xref:core/providers/in-memory/index) | -| [Microsoft.EntityFrameworkCore.Cosmos](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Cosmos) | Azure Cosmos DB SQL API | [EF Core Project](https://github.com/aspnet/EntityFrameworkCore/) (Microsoft) | Preview only | [blog](https://blogs.msdn.microsoft.com/dotnet/2018/10/17/announcing-entity-framework-core-2-2-preview-3/) | +| [Microsoft.EntityFrameworkCore.Cosmos](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Cosmos) | Azure Cosmos DB SQL API | [EF Core Project](https://github.com/aspnet/EntityFrameworkCore/) (Microsoft) | | [docs](xref:core/providers/cosmos/index) | | [Npgsql.EntityFrameworkCore.PostgreSQL](https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL) | PostgreSQL | [Npgsql Development Team](https://github.com/npgsql) | | [docs](http://www.npgsql.org/efcore/index.html) | | [Pomelo.EntityFrameworkCore.MySql](https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql) | MySQL, MariaDB | [Pomelo Foundation Project](https://github.com/PomeloFoundation) | | [readme](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/blob/master/README.md) | | [Pomelo.EntityFrameworkCore.MyCat](https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MyCat) | MyCAT Server | [Pomelo Foundation Project](https://github.com/PomeloFoundation) | Prerelease only | [readme](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MyCat/blob/master/README.md) | diff --git a/entity-framework/index.md b/entity-framework/index.md index 5a39670fd0..8878c34aa1 100644 --- a/entity-framework/index.md +++ b/entity-framework/index.md @@ -201,6 +201,9 @@ uid: index

SQLite

+

+ Cosmos +

more…

diff --git a/entity-framework/toc.md b/entity-framework/toc.md index e00861a4e6..b7a8d6a416 100644 --- a/entity-framework/toc.md +++ b/entity-framework/toc.md @@ -123,6 +123,9 @@ ##### [Memory-Optimized Tables](core/providers/sql-server/memory-optimized-tables.md) #### [SQLite](core/providers/sqlite/index.md) ##### [SQLite Limitations](core/providers/sqlite/limitations.md) +#### [Cosmos](core/providers/cosmos/index.md) +##### [Working with Unstructured Data](core/providers/cosmos/unstructured-data.md) +##### [Cosmos Limitations](core/providers/cosmos/limitations.md) #### [InMemory (for Testing)](core/providers/in-memory/index.md) #### [Writing a Database Provider](core/providers/writing-a-provider.md) #### [Provider-impacting changes](core/providers/provider-log.md) diff --git a/samples/core/Cosmos/Cosmos.csproj b/samples/core/Cosmos/Cosmos.csproj new file mode 100644 index 0000000000..e77625b54a --- /dev/null +++ b/samples/core/Cosmos/Cosmos.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp3.0 + + + + + + + diff --git a/samples/core/Cosmos/ModelBuilding/Distributor.cs b/samples/core/Cosmos/ModelBuilding/Distributor.cs new file mode 100644 index 0000000000..268b901be0 --- /dev/null +++ b/samples/core/Cosmos/ModelBuilding/Distributor.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Cosmos.ModelBuilding +{ + #region Distributor + public class Distributor + { + public int Id { get; set; } + public ICollection ShippingCenters { get; set; } + } + #endregion +} diff --git a/samples/core/Cosmos/ModelBuilding/Order.cs b/samples/core/Cosmos/ModelBuilding/Order.cs new file mode 100644 index 0000000000..7b1ae78e87 --- /dev/null +++ b/samples/core/Cosmos/ModelBuilding/Order.cs @@ -0,0 +1,12 @@ +namespace Cosmos.ModelBuilding +{ + #region Order + public class Order + { + public int Id { get; set; } + public int? TrackingNumber { get; set; } + //public string PartitionKey { get; set; } + public StreetAddress ShippingAddress { get; set; } + } + #endregion +} diff --git a/samples/core/Cosmos/ModelBuilding/OrderContext.cs b/samples/core/Cosmos/ModelBuilding/OrderContext.cs new file mode 100644 index 0000000000..b1900fb194 --- /dev/null +++ b/samples/core/Cosmos/ModelBuilding/OrderContext.cs @@ -0,0 +1,50 @@ +using Microsoft.EntityFrameworkCore; + +namespace Cosmos.ModelBuilding +{ + public class OrderContext : DbContext + { + public DbSet Orders { get; set; } + public DbSet Distributors { get; set; } + + #region Configuration + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseCosmos( + "https://localhost:8081", + "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", + databaseName: "OrdersDB"); + #endregion + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + #region DefaultContainer + modelBuilder.HasDefaultContainer("Store"); + #endregion + + #region Container + modelBuilder.Entity() + .ToContainer("Orders"); + #endregion + + #region PartitionKey + //modelBuilder.Entity() + // .HasPartitionKey(o => o.PartitionKey); + #endregion + + #region PropertyNames + modelBuilder.Entity().OwnsOne( + o => o.ShippingAddress, + sa => + { + sa.ToJsonProperty("Address"); + sa.Property(p => p.Street).ToJsonProperty("ShipsToStreet"); + sa.Property(p => p.City).ToJsonProperty("ShipsToCity"); + }); + #endregion + + #region OwnsMany + modelBuilder.Entity().OwnsMany(p => p.ShippingCenters); + #endregion + } + } +} diff --git a/samples/core/Cosmos/ModelBuilding/Sample.cs b/samples/core/Cosmos/ModelBuilding/Sample.cs new file mode 100644 index 0000000000..ef21988881 --- /dev/null +++ b/samples/core/Cosmos/ModelBuilding/Sample.cs @@ -0,0 +1,113 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Cosmos.ModelBuilding +{ + public static class Sample + { + public static async Task Run() + { + Console.WriteLine(); + Console.WriteLine("Getting started with Cosmos:"); + Console.WriteLine(); + + #region HelloCosmos + var londonOrder = new Order + { + Id = 1, + ShippingAddress = new StreetAddress { City = "London", Street = "221 B Baker St" } + }; + + using (var context = new OrderContext()) + { + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + + context.Add(londonOrder); + + 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}"); + } + #endregion + + #region PartitionKey + //using (var context = new OrderContext()) + //{ + // context.Add(new Order + // { + // Id = 2, + // ShippingAddress = new StreetAddress { City = "New York", Street = "11 Wall Street" }, + // PartitionKey = "1" + // }); + + // context.SaveChangesAsync(); + //} + + //using (var context = new OrderContext()) + //{ + // var order = context.Orders.LastAsync(); + // Console.WriteLine($"Last order will ship to: {order.ShippingAddress.Street}, {order.ShippingAddress.City}"); + //} + #endregion + + #region OwnedCollection + using (var context = new OrderContext()) + { + context.Add(new Distributor + { + Id = 1, + ShippingCenters = new HashSet { + new StreetAddress { City = "Phoenix", Street = "500 S 48th Street" }, + new StreetAddress { City = "Anaheim", Street = "5650 Dolly Ave" } + } + }); + + await context.SaveChangesAsync(); + } + #endregion + + #region ImpliedProperties + 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})"); + } + #endregion + + #region Attach + using (var context = new OrderContext()) + { + var orderEntry = context.Add(londonOrder); + orderEntry.State = EntityState.Unchanged; + + londonOrder.ShippingAddress.Street = "3 Abbey Road"; + + await context.SaveChangesAsync(); + } + + using (var context = new OrderContext()) + { + var order = await context.Orders.FirstAsync(); + Console.WriteLine($"First order will now ship to: {order.ShippingAddress.Street}, {order.ShippingAddress.City}"); + + var orderEntry = context.Entry(order); + var idProperty = orderEntry.Property("id"); + Console.WriteLine($"The order 'id' is: {idProperty.CurrentValue}"); + } + #endregion + } + } +} diff --git a/samples/core/Cosmos/ModelBuilding/StreetAddress.cs b/samples/core/Cosmos/ModelBuilding/StreetAddress.cs new file mode 100644 index 0000000000..8f8aad4d0f --- /dev/null +++ b/samples/core/Cosmos/ModelBuilding/StreetAddress.cs @@ -0,0 +1,10 @@ +namespace Cosmos.ModelBuilding +{ + #region StreetAddress + public class StreetAddress + { + public string Street { get; set; } + public string City { get; set; } + } + #endregion +} diff --git a/samples/core/Cosmos/Program.cs b/samples/core/Cosmos/Program.cs new file mode 100644 index 0000000000..84843991a0 --- /dev/null +++ b/samples/core/Cosmos/Program.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; + +namespace Cosmos +{ + class Program + { + static async Task Main() + { + await ModelBuilding.Sample.Run(); + await UnstructuredData.Sample.Run(); + } + } +} diff --git a/samples/core/Cosmos/UnstructuredData/Sample.cs b/samples/core/Cosmos/UnstructuredData/Sample.cs new file mode 100644 index 0000000000..e17848cbd6 --- /dev/null +++ b/samples/core/Cosmos/UnstructuredData/Sample.cs @@ -0,0 +1,89 @@ +using Cosmos.ModelBuilding; +using Microsoft.Azure.Cosmos; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json.Linq; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Cosmos.UnstructuredData +{ + public static class Sample + { + public static async Task Run() + { + Console.WriteLine(); + Console.WriteLine("Unstructured data:"); + Console.WriteLine(); + + #region Unmapped + using (var context = new OrderContext()) + { + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + + var order = new Order + { + Id = 1, + ShippingAddress = new StreetAddress { City = "London", Street = "221 B Baker St" } + }; + + context.Add(order); + + await context.SaveChangesAsync(); + } + + using (var context = new OrderContext()) + { + var order = await context.Orders.FirstAsync(); + var orderEntry = context.Entry(order); + var jsonProperty = orderEntry.Property("__jObject"); + + jsonProperty.CurrentValue["BillingAddress"] = "Clarence House"; + + orderEntry.State = EntityState.Modified; + + await context.SaveChangesAsync(); + } + + using (var context = new OrderContext()) + { + var order = await context.Orders.FirstAsync(); + var orderEntry = context.Entry(order); + var jsonProperty = orderEntry.Property("__jObject"); + + Console.WriteLine($"First order will be billed to: {jsonProperty.CurrentValue["BillingAddress"]}"); + } + #endregion + + #region CosmosClient + using (var context = new OrderContext()) + { + var cosmosClient = context.Database.GetCosmosClient(); + var database = cosmosClient.GetDatabase("OrdersDB"); + var container = database.GetContainer("Orders"); + + var resultSet = container.GetItemQueryIterator(new QueryDefinition("select * from o")); + var order = (await resultSet.ReadNextAsync()).First(); + + Console.WriteLine($"First order JSON: {order}"); + + order.Remove("TrackingNumber"); + + await container.ReplaceItemAsync(order, order["id"].ToString()); + } + #endregion + + #region MissingProperties + using (var context = new OrderContext()) + { + var orders = await context.Orders.ToListAsync(); + var sortedOrders = await context.Orders.OrderBy(o => o.TrackingNumber).ToListAsync(); + + Console.WriteLine($"Number of orders: {orders.Count}"); + Console.WriteLine($"Number of sorted orders: {sortedOrders.Count}"); + } + #endregion + } + } +} diff --git a/samples/core/Samples.sln b/samples/core/Samples.sln index 3002c6db19..037bb1e8fa 100644 --- a/samples/core/Samples.sln +++ b/samples/core/Samples.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2027 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29230.47 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UWP", "UWP", "{2D7D0958-8722-40E5-9BCB-A96B096E7534}" EndProject @@ -45,7 +45,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp.NewDb", "GetStar EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApp.ExistingDb", "GetStarted\FullNet\ConsoleApp.ExistingDb\ConsoleApp.ExistingDb.csproj", "{01AEB498-D8A2-4DFA-BE6D-6ED24511ED64}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Querying", "Querying\Querying\Querying.csproj", "{7DEB577A-52B8-4475-A109-689FD515AEEA}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Querying", "Querying\Querying.csproj", "{7DEB577A-52B8-4475-A109-689FD515AEEA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConnectionResiliency", "Miscellaneous\ConnectionResiliency\ConnectionResiliency.csproj", "{73D58479-A7E6-4867-8A73-0E07E96C6117}" EndProject @@ -77,6 +77,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QueryTypes", "QueryTypes\Qu EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TableSplitting", "Modeling\TableSplitting\TableSplitting.csproj", "{63685B9A-1233-4B44-AAC1-8DDD4B16B65D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cosmos", "Cosmos\Cosmos.csproj", "{35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -479,6 +481,22 @@ Global {63685B9A-1233-4B44-AAC1-8DDD4B16B65D}.Release|x64.Build.0 = Release|Any CPU {63685B9A-1233-4B44-AAC1-8DDD4B16B65D}.Release|x86.ActiveCfg = Release|Any CPU {63685B9A-1233-4B44-AAC1-8DDD4B16B65D}.Release|x86.Build.0 = Release|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Debug|ARM.ActiveCfg = Debug|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Debug|ARM.Build.0 = Debug|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Debug|x64.ActiveCfg = Debug|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Debug|x64.Build.0 = Debug|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Debug|x86.ActiveCfg = Debug|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Debug|x86.Build.0 = Debug|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Release|Any CPU.Build.0 = Release|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Release|ARM.ActiveCfg = Release|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Release|ARM.Build.0 = Release|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Release|x64.ActiveCfg = Release|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Release|x64.Build.0 = Release|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Release|x86.ActiveCfg = Release|Any CPU + {35091DAC-FAE6-4384-B499-CAF2D1EA4FCF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE