Skip to content

Latest commit

 

History

History
170 lines (120 loc) · 9.65 KB

README.md

File metadata and controls

170 lines (120 loc) · 9.65 KB

Beef.Data.Cosmos

NuGet version

Adds additional capabilities extending Microsoft.Azure.Cosmos that standardise and simply usage of the Cosmos Database for Beef.


Database

To encapsulate the Cosmos database access the CosmosDb or CosmosDbBase is inherited to enable.

The following demonstrates the usage:

public class MyCosmosDb : CosmosDb<MyCosmosDb>
{
    public MyCosmosDb() : base(new Microsoft.Azure.Cosmos("https://localhost:8081", "C2=="), "Beef.UnitTest", true)
    { }
}

Mapping

A key feature is the mapping of a .NET entity to/from a cosmos-oriented .NET model (these can be the same). The CosmosDbMapper will enable:

  • Property to/from mapping, including naming differences.
  • Property to/from data type conversions.
  • Entity to/from one or more property mappings.

Also, specific mappings can be configured to only be performed when performing a specific operation type; e.g. Create or Update, etc.

The following demonstrates the usage:

public partial class CosmosMapper : CosmosDbMapper<Robot, Robot, CosmosMapper>
{
    /// <summary>
    /// Initializes a new instance of the <see cref="CosmosMapper"/> class.
    /// </summary>
    public CosmosMapper()
    {
        Property(s => s.Id, d => d.Id).SetUniqueKey(true);
        Property(s => s.ModelNo, d => d.ModelNo);
        Property(s => s.SerialNo, d => d.SerialNo);
        Property(s => s.EyeColorSid, d => d.EyeColorSid);
        Property(s => s.PowerSourceSid, d => d.PowerSourceSid);
        AddStandardProperties();
        CosmosMapperCtor();
    }
}

Operation arguments

The CosmosDbArgs provides the required Container operation arguments. As a general rule the CosmosDbArgs is created from the CosmosDbMapper:

Property Description
ContainerId The Cosmos Container identifier.
PartitionKey The PartitionKey (defaults to PartitionKey.None).
Paging The paging configuration (used by Query operation only).
ItemRequestOptions The ItemRequestOptions used for Get, Create, Update and Delete.
QueryRequestOptions The QueryRequestOptions used for Query only.
NullOnNotFoundResponse Indicates that a null is to be returned where the response has an HttpStatusCode.NotFound on a Get.
SetIdentifierOnCreate Indicates whether to the set (override) the identifier on Create where the entity implements IIdentifer.
SetAuthorizedFilter Sets the filter (IQueryable) for all operations to ensure consistent authorisation is applied. Applies automatically to all queries, in that the filter is applied each time a CosmosDbQuery or CosmosDbValueQuery is executed. Additionally, the filter is applied to the standard Get, Create, Update and Delete (CRUD) operations to ensure only authorised data is accessed and modified.

The following demonstrates the usage:

var args1 = CosmosMapper.Default.CreateArgs("Persons");
var args2 = CosmosMapper.Default.CreateArgs("Persons", paging);

Container-based

A CosmosDbContainer (and CosmosDbValueContainer for CosmosDbValue) enables all of the CRUD and Query access for a configured Cosmos Container; versus, having to specify the container identifier per operation.

Examples as follows:

public class CosmosDb : CosmosDbBase
{
    public CosmosDb() : base(new Microsoft.Azure.Cosmos("https://localhost:8081", "C2=="), "Beef.UnitTest", true)
    {
        Persons = new CosmosDbContainer<Person, Person>(this, CosmosMapper.Default.CreateArgs("Persons"));
    }

    public CosmosDbContainer<Person, Person> Persons { get; private set; }
}

...

var db = new CosmosDb();
var v = await db.Persons.GetAsync(Guid.NewGuid());

CRUD

The primary data persistence activities are CRUD (Create, Read, Update and Delete) related; CosmosDbContainer (and CosmosDbValueContainer for CosmosDbValue) enable:

Operation Description
GetAsync Gets the entity for the specified key where found; otherwise, null (default) or NotFoundException depending on the corresponding CosmosDbArgs.NullOnNotFoundResponse.
CreateAsync Creates the entity. Automatically updates the Created* fields of IChangeLog where implemented. Where the the corresponding CosmosDbArgs.SetIdentifierOnCreate is true (default), then the Cosmos Id will be set to Guid.NewGuid (overridding any prior value).
UpdateAsync Updates the entity. Automatically updates the Updated* fields of IChangeLog where implemented; also ensuring that the existing Created* fields are not changed.
DeleteAsync Deletes the entity. Given a delete is idempotent it will be successful even where the entity does not exist.

Additional information:

  • Where the entity implements IETag then the UpdateAsync will be performed with an If-Match header; and a corresponding ConcurrencyException will be thrown where it does not match. Note: for the ETag to function correctly the JSON name on the model must be _etag.
  • Where uniqueness has been defined for the Container and a create or update results in a duplicate a DuplicateException will be thrown.

Query

More advanced query operations are enabled via by the CosmosDbQuery (and CosmosDbValueQuery for CosmosDbValue) which further extends on the LINQ capabilities provided by the Container. This supports an overload where a query Func can be added to simplify the likes of filtering, etc. where needed:

Operation Description
AsQueryable Gets a prepared IQueryable (with any CosmosDbValue.Type filtering as applicable).
Note: for this reason this is the recommended approach for all ad-hoc queries.
Note: CosmosDbArgs.Paging is not supported and must be applied using the provided IQueryable.Paging.
SelectFirst Selects the first item.*
SelectFirstOrDefault Selects the first item or default.*
SelectSingle Selects a single item.*
SelectSingleOrDefault Selects a single item or default.*
SelectQuery Select multiple items and either creates, or updates an existing, collection. Where the corresponding CosmosDbArgs.Paging is provided the configured paging, and optional get count, will be enacted.

* These are provided for use versus than the default IQueryable equivalents (which are currently not supported) as they will only internally page one or two items accordingly to minimise query and data costs. Paging cannot be applied more than once as it will result in a invalid sub-query.


Cosmos specific model

Where the Entity does not naturally map to a Cosmos Model a couple of options are provided:

  • Inherit Model from CosmosDbModelBase; this provides the basic Id, _etag and ttl.
  • Use the CosmosDbValueContainer that in turn leverages the CosmosDbValue for persisting the Model Value. This inherits from CosmosDbModelBase, and extends by adding a Type (enables values with multiple types to be persisted in a single containger; for example, reference data), and the Value itself.

Row-level Authorisation

Additional row-level like authorisation can be applied to all CRUD and Query operations. The CosmosDbArgs.SetAuthorizedFilter (described here) defines the authorization filter. This should be set before any operation is performed. The beef code-generation provides a _onDataArgsCreate method that can be set to perform; this will be invoked each time a CosmosDbArgs is instantiated.

Example as follows. This demonstrates filtering a Content entity by allowable ContentType that has been added to the ExecutionContext set when the user is configured at startup:

_onDataArgsCreate = OnDataArgsCreate;

...

private void OnDataArgsCreate(ICosmosDbArgs dbArgs)
{
    var cda = (CosmosDbArgs<Content, Content>)dbArgs;
    cda.SetAuthorizedFilter((q) => ((IQueryable<CosmosDbValue<Content>>)q).Where(c => ExecutionContext.Current.ContentType.Contains(c.Value.ContentType)));
}

Local emulator

Given there are costs associated with using Cosmos DB, consider using the local emulator for development and testing purposes: https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator