public static void Write(OutputModel outputModel, DatabasePlan databasePlan, PartitionPlan partitionPlan) { var s = $@" namespace {databasePlan.Namespace}; public class {partitionPlan.QueryUnionsClassName} : Cosmogenesis.Core.DbQueryBase {{ protected virtual {databasePlan.Namespace}.{databasePlan.DbClassName} {databasePlan.DbClassName} {{ get; }} = default!; protected virtual {databasePlan.Namespace}.{partitionPlan.QueryBuilderClassName} {partitionPlan.QueryBuilderClassName} {{ get; }} = default!; /// <summary>Mocking constructor</summary> protected {partitionPlan.QueryUnionsClassName}() {{ }} internal protected {partitionPlan.QueryUnionsClassName}( {databasePlan.Namespace}.{databasePlan.DbClassName} {databasePlan.DbClassNameArgument}, {databasePlan.Namespace}.{partitionPlan.QueryBuilderClassName} {partitionPlan.QueryBuilderClassNameArgument}) : base( dbBase: {databasePlan.DbClassNameArgument}, queryBuilder: {partitionPlan.QueryBuilderClassNameArgument}) {{ this.{databasePlan.DbClassName} = {databasePlan.DbClassNameArgument} ?? throw new System.ArgumentNullException(nameof({databasePlan.DbClassNameArgument})); this.{partitionPlan.QueryBuilderClassName} = {partitionPlan.QueryBuilderClassNameArgument} ?? throw new System.ArgumentNullException(nameof({partitionPlan.QueryBuilderClassNameArgument})); }} {string.Concat(partitionPlan.Unions.Select(x => Query(databasePlan, partitionPlan, x)))} }} "; outputModel.Context.AddSource($"partition_{partitionPlan.QueryUnionsClassName}.cs", s); }
static string Unions(DatabasePlan databasePlan, PartitionPlan partitionPlan) => partitionPlan.Unions.Count == 0 ? "" : $@" {databasePlan.Namespace}.{partitionPlan.ReadOrThrowUnionsClassName}? unions; public virtual {databasePlan.Namespace}.{partitionPlan.ReadOrThrowUnionsClassName} Unions => this.unions ??= new( {databasePlan.DbClassNameArgument}: this.{databasePlan.DbClassName}, partitionKey: this.PartitionKey); ";
public static void Write(OutputModel outputModel, DatabasePlan databasePlan, PartitionPlan partitionPlan) { if (!partitionPlan.Documents.Any(x => x.IsMutable || x.IsTransient)) { return; } var s = $@" namespace {databasePlan.Namespace}; public class {partitionPlan.CreateOrReplaceClassName} {{ protected virtual {databasePlan.Namespace}.{partitionPlan.ClassName} {partitionPlan.ClassName} {{ get; }} = default!; /// <summary>Mocking constructor</summary> protected {partitionPlan.CreateOrReplaceClassName}() {{ }} internal protected {partitionPlan.CreateOrReplaceClassName}({databasePlan.Namespace}.{partitionPlan.ClassName} {partitionPlan.ClassNameArgument}) {{ this.{partitionPlan.ClassName} = {partitionPlan.ClassNameArgument} ?? throw new System.ArgumentNullException(nameof({partitionPlan.ClassNameArgument})); }} {string.Concat(partitionPlan.Documents.Select(x => CreateOrReplace(partitionPlan, x)))} }} "; outputModel.Context.AddSource($"partition_{partitionPlan.CreateOrReplaceClassName}.cs", s); }
public static void Write(OutputModel outputModel, DatabasePlan databasePlan, PartitionPlan partitionPlan) { var s = $@" namespace {databasePlan.Namespace}; public class {partitionPlan.QueryBuilderUnionsClassName} : Cosmogenesis.Core.DbQueryBuilderBase {{ /// <summary>Mocking constructor</summary> protected {partitionPlan.QueryBuilderUnionsClassName}() {{ }} internal protected {partitionPlan.QueryBuilderUnionsClassName}( {databasePlan.Namespace}.{databasePlan.DbClassName} {databasePlan.DbClassNameArgument}, Microsoft.Azure.Cosmos.PartitionKey? partitionKey) : base( dbBase: {databasePlan.DbClassNameArgument}, partitionKey: partitionKey) {{ }} {string.Concat(partitionPlan.Unions.Select(BuildQuery))} }} "; outputModel.Context.AddSource($"partition_{partitionPlan.QueryBuilderUnionsClassName}.cs", s); }
static string Unions(DatabasePlan databasePlan, PartitionPlan partitionPlan) => partitionPlan.Unions.Count == 0 ? "" : $@" {databasePlan.Namespace}.{partitionPlan.QueryUnionsClassName}? unions; public virtual {databasePlan.Namespace}.{partitionPlan.QueryUnionsClassName} Unions => this.unions ??= new( {databasePlan.DbClassNameArgument}: this.{databasePlan.DbClassName}, {partitionPlan.QueryBuilderClassNameArgument}: this.{partitionPlan.QueryBuilderClassName}); ";
public static void Write(OutputModel outputModel, DatabasePlan databasePlan, PartitionPlan partitionPlan) { var s = $@" namespace {databasePlan.Namespace}; public class {partitionPlan.QueryBuilderClassName} : Cosmogenesis.Core.DbQueryBuilderBase {{ protected virtual {databasePlan.Namespace}.{databasePlan.DbClassName} {databasePlan.DbClassName} {{ get; }} = default!; /// <summary>Mocking constructor</summary> protected {partitionPlan.QueryBuilderClassName}() {{ }} internal protected {partitionPlan.QueryBuilderClassName}( {databasePlan.Namespace}.{databasePlan.DbClassName} {databasePlan.DbClassNameArgument}, Microsoft.Azure.Cosmos.PartitionKey? partitionKey) : base( dbBase: {databasePlan.DbClassNameArgument}, partitionKey: partitionKey) {{ {databasePlan.DbClassName} = {databasePlan.DbClassNameArgument} ?? throw new System.ArgumentNullException(nameof({databasePlan.DbClassNameArgument})); }} {string.Concat(partitionPlan.Documents.Select(BuildQuery))} {Unions(databasePlan, partitionPlan)} }} "; if (partitionPlan.Unions.Any()) { QueryBuilderUnionsWriter.Write(outputModel, databasePlan, partitionPlan); } outputModel.Context.AddSource($"partition_{partitionPlan.QueryBuilderClassName}.cs", s); }
static string Query(DatabasePlan databasePlan, PartitionPlan partitionPlan, UnionPlan unionPlan) => $@" /// <summary> /// Build and execute a query filtered to {unionPlan.CommonName} documents. /// {unionPlan.CommonName} is a union of: {string.Join(", ", unionPlan.Documents.Select(x => x.ClassName))} /// <see cref=""https://github.com/Azure/azure-cosmos-dotnet-v3/blob/bb72ba5786d99d928b4774e16810f2655029e8a2/Microsoft.Azure.Cosmos/src/Linq/CosmosLinqExtensions.cs"" /> /// </summary> public virtual System.Collections.Generic.IAsyncEnumerable<T> {unionPlan.CommonName.Pluralize()}<T>( System.Func<System.Linq.IQueryable<{unionPlan.FullCommonTypeName}>, System.Linq.IQueryable<T>> createQuery, System.Threading.CancellationToken cancellationToken = default) => this.{databasePlan.DbClassName} .ExecuteQueryAsync( query: createQuery(this.{partitionPlan.QueryBuilderClassName}.Unions.{unionPlan.CommonName.Pluralize()}()), cancellationToken: cancellationToken); /// <summary> /// Execute a query filtered to {unionPlan.CommonName} documents. /// {unionPlan.CommonName} is a union of: {string.Join(", ", unionPlan.Documents.Select(x => x.ClassName))} /// </summary> public virtual System.Collections.Generic.IAsyncEnumerable<{unionPlan.FullCommonTypeName}> {unionPlan.CommonName.Pluralize()}( System.Threading.CancellationToken cancellationToken = default) => this.{databasePlan.DbClassName} .ExecuteQueryAsync( query: this.{partitionPlan.QueryBuilderClassName}.Unions.{unionPlan.CommonName.Pluralize()}(), cancellationToken: cancellationToken); ";
public static void Write(OutputModel outputModel, DatabasePlan databasePlan, PartitionPlan partitionPlan) { var s = $@" namespace {databasePlan.Namespace}; public class {partitionPlan.ReadClassName} {{ protected virtual {databasePlan.Namespace}.{databasePlan.DbClassName} {databasePlan.DbClassName} {{ get; }} = default!; protected virtual Microsoft.Azure.Cosmos.PartitionKey PartitionKey {{ get; }} = default!; /// <summary>Mocking constructor</summary> protected {partitionPlan.ReadClassName}() {{ }} internal protected {partitionPlan.ReadClassName}( {databasePlan.Namespace}.{databasePlan.DbClassName} {databasePlan.DbClassNameArgument}, Microsoft.Azure.Cosmos.PartitionKey partitionKey) {{ this.{databasePlan.DbClassName} = {databasePlan.DbClassNameArgument} ?? throw new System.ArgumentNullException(nameof({databasePlan.DbClassNameArgument})); this.PartitionKey = partitionKey; }} {string.Concat(partitionPlan.Documents.Select(x => Read(databasePlan, x)))} {Unions(databasePlan, partitionPlan)} }} "; if (partitionPlan.Unions.Count > 0) { ReadUnionsWriter.Write(outputModel, databasePlan, partitionPlan); } outputModel.Context.AddSource($"partition_{partitionPlan.ReadClassName}.cs", s); }
static string Create(PartitionPlan partitionPlan, DocumentPlan documentPlan) => $@" /// <summary> /// Try to create a {documentPlan.ClassName}. /// </summary> /// <exception cref=""Cosmogenesis.Core.DbOverloadedException"" /> /// <exception cref=""Cosmogenesis.Core.DbUnknownStatusCodeException"" /> public virtual System.Threading.Tasks.Task<Cosmogenesis.Core.CreateResult<{documentPlan.FullTypeName}>> {documentPlan.ClassName}Async({documentPlan.PropertiesByName.Values.Where(x => !partitionPlan.GetPkPlan.ArgumentByPropertyName.ContainsKey(x.PropertyName)).AsInputParameters()}) => this.{partitionPlan.ClassName}.CreateAsync({documentPlan.ClassNameArgument}: new {documentPlan.FullTypeName} {{ {partitionPlan.AsSettersFromDocumentPlanAndPartitionClass(documentPlan)} }}); ";
static string ReadOrCreate(PartitionPlan partitionPlan, DocumentPlan documentPlan) => $@" /// <summary> /// Read a {documentPlan.ClassName} document, or create it if it does not yet exist. /// </summary> /// <exception cref=""Cosmogenesis.Core.DbOverloadedException"" /> /// <exception cref=""Cosmogenesis.Core.DbUnknownStatusCodeException"" /> public virtual System.Threading.Tasks.Task<Cosmogenesis.Core.ReadOrCreateResult<{documentPlan.FullTypeName}>> {documentPlan.ClassName}Async({new[] { "bool tryCreateFirst", documentPlan.PropertiesByName.Values.Where(x => !partitionPlan.GetPkPlan.ArgumentByPropertyName.ContainsKey(x.PropertyName)).AsInputParameters() }.JoinNonEmpty()}) => this.{partitionPlan.ClassName}.ReadOrCreateAsync({documentPlan.ClassNameArgument}: new {documentPlan.FullTypeName} {{ {partitionPlan.AsSettersFromDocumentPlanAndPartitionClass(documentPlan)} }}, tryCreateFirst: tryCreateFirst); ";
static string CreateOrReplace(DatabasePlan databasePlan, PartitionPlan partitionPlan) => !partitionPlan.Documents.Any(x => x.IsTransient || x.IsMutable) ? "" : $@" {partitionPlan.CreateOrReplaceClassName}? createOrReplace; /// <summary> /// Methods to create or replace (unconditionally overwrite) documents. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.CreateOrReplaceClassName} CreateOrReplace => this.createOrReplace ??= new(this); ";
static string PkClass(PartitionPlan partitionPlan) => partitionPlan.GetPkPlan.Arguments.Count == 0 ? "" : $@" internal protected struct PkData {{ {string.Concat(partitionPlan.GetPkPlan.Arguments.Select(PkClassProperty))} }} internal protected virtual PkData PartitionKeyData {{ get; }} ";
static string ReadMany(DatabasePlan databasePlan, PartitionPlan partitionPlan) => !partitionPlan.Documents.Any(x => x.GetIdPlan.Arguments.Count > 0) ? "" : $@" {partitionPlan.ReadManyClassName}? readMany; /// <summary> /// Methods to read multiple documents at once, though not necessarily in a single operation. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.ReadManyClassName} ReadMany => this.readMany ??= new( {databasePlan.DbClassNameArgument}: {databasePlan.DbClassName}, partitionKey: this.PartitionKey); ";
static string CreateOrReplace(PartitionPlan partitionPlan, DocumentPlan documentPlan) => !documentPlan.IsTransient && !documentPlan.IsMutable ? "" : $@" /// <summary> /// Create or replace (unconditionally overwrite) a {documentPlan.ClassName}. /// </summary> /// <exception cref=""Cosmogenesis.Core.DbOverloadedException"" /> /// <exception cref=""Cosmogenesis.Core.DbUnknownStatusCodeException"" /> public virtual System.Threading.Tasks.Task<Cosmogenesis.Core.CreateOrReplaceResult<{documentPlan.FullTypeName}>> {documentPlan.ClassName}Async({documentPlan.PropertiesByName.Values.Where(x => !partitionPlan.GetPkPlan.ArgumentByPropertyName.ContainsKey(x.PropertyName)).AsInputParameters()}) => this.{partitionPlan.ClassName}.CreateOrReplaceAsync(new {documentPlan.FullTypeName} {{ {partitionPlan.AsSettersFromDocumentPlanAndPartitionClass(documentPlan)} }}); ";
static string Delete(DatabasePlan databasePlan, PartitionPlan partitionPlan, DocumentPlan documentPlan) => !documentPlan.IsTransient ? "" : $@" /// <summary> /// Queue a {documentPlan.ClassName} for deletion in the batch /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.BatchClassName} Delete({documentPlan.FullTypeName} {documentPlan.ClassNameArgument}) {{ this.DeleteCore(item: {documentPlan.ClassNameArgument}); return this; }} ";
static string Replace(DatabasePlan databasePlan, PartitionPlan partitionPlan, DocumentPlan documentPlan) => !documentPlan.IsMutable ? "" : $@" /// <summary> /// Queue a {documentPlan.ClassName} for replacement in the batch /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.BatchClassName} Replace({documentPlan.FullTypeName} {documentPlan.ClassNameArgument}) {{ this.ReplaceCore(item: {documentPlan.ClassNameArgument}, type: {documentPlan.ConstDocType}); return this; }} ";
public static string AsSettersFromDocumentPlanAndPartitionClass(this PartitionPlan partitionPlan, DocumentPlan documentPlan) { var setters = documentPlan .PropertiesByName .Values .Where(x => !partitionPlan.GetPkPlan.ArgumentByPropertyName.ContainsKey(x.PropertyName)) .Select(x => $"{x.PropertyName} = {x.ArgumentName}"); var key = partitionPlan .GetPkPlan .Arguments .Select(x => $"{x.PropertyName} = this.{partitionPlan.ClassName}.PartitionKeyData.{x.PropertyName}"); return(setters.Concat(key).JoinNonEmpty()); }
static string ReadOrCreate(PartitionPlan partitionPlan, DocumentPlan documentPlan) => $@" /// <summary> /// Read a {documentPlan.ClassName} document, or create it if it does not yet exist. /// .id must be set if there is no stable id generator defined /// .pk, .CreationDate and .Type are set automatically /// </summary> /// <exception cref=""Cosmogenesis.Core.DbOverloadedException"" /> /// <exception cref=""Cosmogenesis.Core.DbUnknownStatusCodeException"" /> internal protected virtual System.Threading.Tasks.Task<Cosmogenesis.Core.ReadOrCreateResult<{documentPlan.FullTypeName}>> ReadOrCreateAsync(bool tryCreateFirst, {documentPlan.FullTypeName} {documentPlan.ClassNameArgument}) {{ {DocumentModelWriter.CreateAndCheckPkAndId(partitionPlan, documentPlan, documentPlan.ClassNameArgument)} return this.ReadOrCreateItemAsync(item: {documentPlan.ClassNameArgument}, type: {documentPlan.ConstDocType}, tryCreateFirst: tryCreateFirst); }} ";
static string Partition(PartitionPlan partitionPlan) => $@" public class {partitionPlan.BatchHandlersClassName} {{ /// <summary>Mocking constructor</summary> protected {partitionPlan.BatchHandlersClassName}() {{ }} public {partitionPlan.BatchHandlersClassName}({partitionPlan.Documents.OrderBy(x => x.ClassName).Select(ConstructorArg).JoinNonEmpty()}) {{ {string.Concat(partitionPlan.Documents.Select(AssignArg))} }} {string.Concat(partitionPlan.Documents.Select(Handler))} }} public virtual {partitionPlan.BatchHandlersClassName}? {partitionPlan.Name} {{ get; }} ";
static string CreateOrReplace(PartitionPlan partitionPlan, DocumentPlan documentPlan) => !documentPlan.IsMutable && !documentPlan.IsTransient ? "" : $@" /// <summary> /// Create or replace (unconditionally overwrite) a {documentPlan.ClassName}. /// .id must be set if there is no stable id generator defined /// .pk, .CreationDate and .Type are set automatically /// </summary> /// <exception cref=""Cosmogenesis.Core.DbOverloadedException"" /> /// <exception cref=""Cosmogenesis.Core.DbUnknownStatusCodeException"" /> internal protected virtual System.Threading.Tasks.Task<Cosmogenesis.Core.CreateOrReplaceResult<{documentPlan.FullTypeName}>> CreateOrReplaceAsync({documentPlan.FullTypeName} {documentPlan.ClassNameArgument}) {{ {DocumentModelWriter.CreateAndCheckPkAndId(partitionPlan, documentPlan, documentPlan.ClassNameArgument)} return this.CreateOrReplaceItemAsync(item: {documentPlan.ClassNameArgument}, type: {documentPlan.ConstDocType}); }} ";
static string Create(DatabasePlan databasePlan, PartitionPlan partitionPlan, DocumentPlan documentPlan) => $@" /// <summary> /// Queue a {documentPlan.ClassName} for creation in the batch /// </summary> protected virtual {databasePlan.Namespace}.{partitionPlan.BatchClassName} Create({documentPlan.FullTypeName} {documentPlan.ClassNameArgument}) {{ {DocumentModelWriter.CreateAndCheckPkAndId(partitionPlan, documentPlan, documentPlan.ClassNameArgument)} this.CreateCore(item: {documentPlan.ClassNameArgument}, type: {documentPlan.ConstDocType}); return this; }} /// <summary> /// Queue a {documentPlan.ClassName} for creation in the batch /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.BatchClassName} Create{documentPlan.ClassName}({documentPlan.PropertiesByName.Values.Where(x => !partitionPlan.GetPkPlan.ArgumentByPropertyName.ContainsKey(x.PropertyName)).AsInputParameters()}) => this.Create({documentPlan.ClassNameArgument}: new {documentPlan.FullTypeName} {{ {partitionPlan.AsSettersFromDocumentPlanAndPartitionClass(documentPlan)} }}); ";
public static string CreateAndCheckPkAndId(PartitionPlan partitionPlan, DocumentPlan documentPlan, string paramTypeName) => $@" if ({paramTypeName} is null) {{ throw new System.ArgumentNullException(nameof({paramTypeName})); }} var calculatedPk = {partitionPlan.GetPkPlan.FullMethodName}({partitionPlan.GetPkPlan.DocumentToParametersMapping(paramTypeName)}); var calculatedId = Cosmogenesis.Core.DbDocHelper.GetValidId({documentPlan.GetIdPlan.FullMethodName}({documentPlan.GetIdPlan.DocumentToParametersMapping(paramTypeName)})); if ({paramTypeName}.id is null) {{ {paramTypeName}.id = calculatedId ?? throw new System.InvalidOperationException(""The generated document id cannot be null""); }} else if ({paramTypeName}.id != calculatedId) {{ throw new System.InvalidOperationException(""The document .id property does not match the calculated document id""); }} if ({paramTypeName}.pk is null) {{ {paramTypeName}.pk = calculatedPk ?? throw new System.InvalidOperationException(""The generated partition key cannot be null""); }} else if ({paramTypeName}.pk != calculatedPk) {{ throw new System.InvalidOperationException(""The document .pk property does not match the calculated document partition key""); }} ";
static string PkClassSetter(PartitionPlan partitionPlan) => partitionPlan.GetPkPlan.Arguments.Count == 0 ? "" : "this.PartitionKeyData = pkData;";
static string ConstructorKeyParameter(PartitionPlan partitionPlan) => partitionPlan.GetPkPlan.Arguments.Count == 0 ? "" : $@" in PkData pkData";
public static void Write(OutputModel outputModel, DatabasePlan databasePlan, PartitionPlan partitionPlan) { var s = $@" namespace {databasePlan.Namespace}; public class {partitionPlan.BatchClassName} : Cosmogenesis.Core.DbBatchBase {{ protected virtual {databasePlan.Namespace}.{partitionPlan.ClassName} {partitionPlan.ClassName} {{ get; }} = default!; /// <summary>Mocking constructor</summary> protected {partitionPlan.BatchClassName}() {{ }} internal protected {partitionPlan.BatchClassName}( Microsoft.Azure.Cosmos.TransactionalBatch transactionalBatch, string partitionKey, bool validateStateBeforeSave, {databasePlan.Namespace}.{partitionPlan.ClassName} {partitionPlan.ClassNameArgument}) : base( transactionalBatch: transactionalBatch, partitionKey: partitionKey, serializer: {databasePlan.Namespace}.{databasePlan.SerializerClassName}.Instance, validateStateBeforeSave: validateStateBeforeSave) {{ this.{partitionPlan.ClassName} = {partitionPlan.ClassNameArgument} ?? throw new System.ArgumentNullException(nameof({partitionPlan.ClassNameArgument})); }} /// <summary> /// Queue a document for creation in the batch. /// Throws InvalidOperationException if the DbDoc does not belong in the partition. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.BatchClassName} CheckAndCreate(Cosmogenesis.Core.DbDoc dbDoc) => dbDoc switch {{ {string.Concat(partitionPlan.Documents.Select(CheckedCreate))} null => throw new System.ArgumentNullException(nameof(dbDoc)), _ => throw new System.InvalidOperationException($""{{dbDoc.GetType().Name}} is not a type stored in this partition"") }}; /// <summary> /// Tries to queue a document for creation in the batch. /// Returns true if queued, or false if the document does not belong in the partition. /// </summary> public virtual bool TryCheckAndCreate(Cosmogenesis.Core.DbDoc dbDoc) => dbDoc switch {{ {string.Concat(partitionPlan.Documents.Select(CheckedCreate))} null => throw new System.ArgumentNullException(nameof(dbDoc)), _ => ({databasePlan.Namespace}.{partitionPlan.BatchClassName}?)null }} != null; /// <summary> /// Queue a document for creation or replacement in the batch. /// Throws InvalidOperationException if the DbDoc does not belong in the partition or is not mutable. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.BatchClassName} CheckAndCreateOrReplace(Cosmogenesis.Core.DbDoc dbDoc) => dbDoc switch {{ {string.Concat(partitionPlan.Documents.Where(x => x.IsMutable || x.IsTransient).Select(CheckedCreateOrReplace))} null => throw new System.ArgumentNullException(nameof(dbDoc)), _ => throw new System.InvalidOperationException($""{{dbDoc.GetType().Name}} is not a mutable type in this partition"") }}; /// <summary> /// Tries to queue a document for creation or replacement in the batch. /// Returns true if queued, or false if the document does not belong in the partition or is not mutable. /// </summary> public virtual bool TryCheckAndCreateOrReplace(Cosmogenesis.Core.DbDoc dbDoc) => dbDoc switch {{ {string.Concat(partitionPlan.Documents.Where(x => x.IsMutable || x.IsTransient).Select(CheckedCreateOrReplace))} null => throw new System.ArgumentNullException(nameof(dbDoc)), _ => ({databasePlan.Namespace}.{partitionPlan.BatchClassName}?)null }} != null; /// <summary> /// Queue a document for replacement in the batch. /// Throws InvalidOperationException if the DbDoc does not belong in the partition or is not mutable. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.BatchClassName} CheckAndReplace(Cosmogenesis.Core.DbDoc dbDoc) => dbDoc switch {{ {string.Concat(partitionPlan.Documents.Where(x => x.IsMutable).Select(CheckedReplace))} null => throw new System.ArgumentNullException(nameof(dbDoc)), _ => throw new System.InvalidOperationException($""{{dbDoc.GetType().Name}} is not a mutable type in this partition"") }}; /// <summary> /// Tries to queue a document for replacement in the batch. /// Returns true if queued, or false if the document does not belong in the partition or is not mutable. /// </summary> public virtual bool TryCheckAndReplace(Cosmogenesis.Core.DbDoc dbDoc) => dbDoc switch {{ {string.Concat(partitionPlan.Documents.Where(x => x.IsMutable).Select(CheckedReplace))} null => throw new System.ArgumentNullException(nameof(dbDoc)), _ => ({databasePlan.Namespace}.{partitionPlan.BatchClassName}?)null }} != null; /// <summary> /// Queue a document for deletion in the batch. /// Throws InvalidOperationException if the DbDoc does not belong in the partition or is not transient. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.BatchClassName} CheckAndDelete(Cosmogenesis.Core.DbDoc dbDoc) => dbDoc switch {{ {string.Concat(partitionPlan.Documents.Where(x => x.IsTransient).Select(CheckedDelete))} null => throw new System.ArgumentNullException(nameof(dbDoc)), _ => throw new System.InvalidOperationException($""{{dbDoc.GetType().Name}} is not a transient type in this partition"") }}; /// <summary> /// Tries to queue a document for deletion in the batch. /// Returns true if queued, or false if the document does not belong in the partition or is not transient. /// </summary> public virtual bool TryCheckAndDelete(Cosmogenesis.Core.DbDoc dbDoc) => dbDoc switch {{ {string.Concat(partitionPlan.Documents.Where(x => x.IsTransient).Select(CheckedDelete))} null => throw new System.ArgumentNullException(nameof(dbDoc)), _ => ({databasePlan.Namespace}.{partitionPlan.BatchClassName}?)null }} != null; {string.Concat(partitionPlan.Documents.Select(x => Create(databasePlan, partitionPlan, x)))} {string.Concat(partitionPlan.Documents.Select(x => CreateOrReplace(databasePlan, partitionPlan, x)))} {string.Concat(partitionPlan.Documents.Select(x => Replace(databasePlan, partitionPlan, x)))} {string.Concat(partitionPlan.Documents.Select(x => Delete(databasePlan, partitionPlan, x)))} }} "; outputModel.Context.AddSource($"partition_{partitionPlan.BatchClassName}.cs", s); }
static string PartitionTypes(PartitionPlan partitionPlan) => $@" public static class {partitionPlan.ClassName} {{ {string.Concat(partitionPlan.Documents.Select(Type))} }} ";
public static void Write(OutputModel outputModel, DatabasePlan databasePlan, PartitionPlan partitionPlan) { var s = $@" namespace {databasePlan.Namespace}; public class {partitionPlan.ClassName} : Cosmogenesis.Core.DbPartitionBase {{ protected virtual {databasePlan.Namespace}.{databasePlan.DbClassName} {databasePlan.DbClassName} {{ get; }} = default!; {PkClass(partitionPlan)} /// <summary>Mocking constructor</summary> protected {partitionPlan.ClassName}() {{ }} internal protected {partitionPlan.ClassName}( {new[] { ConstructorClassParameter(databasePlan), ConstructorKeyParameter(partitionPlan) }.Where(x => !string.IsNullOrEmpty(x)).JoinNonEmpty()}) : base( db: {databasePlan.DbClassNameArgument}, partitionKey: {partitionPlan.GetPkPlan.FullMethodName}({partitionPlan.GetPkPlan.DocumentToParametersMapping("pkData")}), serializer: {databasePlan.Namespace}.{databasePlan.SerializerClassName}.Instance) {{ this.{databasePlan.DbClassName} = {databasePlan.DbClassNameArgument} ?? throw new System.ArgumentNullException(nameof({databasePlan.DbClassNameArgument})); {PkClassSetter(partitionPlan)} }} {databasePlan.Namespace}.{partitionPlan.QueryBuilderClassName}? queryBuilder; /// <summary> /// Methods to build queries for later execution. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.QueryBuilderClassName} QueryBuilder => this.queryBuilder ??= new( {databasePlan.DbClassNameArgument}: this.{databasePlan.DbClassName}, partitionKey: this.PartitionKey); {databasePlan.Namespace}.{partitionPlan.QueryClassName}? query; /// <summary> /// Methods to execute queries. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.QueryClassName} Query => this.query ??= new( {databasePlan.DbClassNameArgument}: this.{databasePlan.DbClassName}, {partitionPlan.QueryBuilderClassNameArgument}: this.QueryBuilder); /// <summary> /// A batch of operations to be executed atomically (or not at all) within a {partitionPlan.Name} in the {databasePlan.Name} database. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.BatchClassName} CreateBatch() => new( transactionalBatch: this.CreateBatchForPartition(), partitionKey: this.PartitionKeyString, validateStateBeforeSave: this.{databasePlan.DbClassName}.ValidateStateBeforeSave, {partitionPlan.ClassNameArgument}: this); {databasePlan.Namespace}.{partitionPlan.ReadClassName}? read; /// <summary> /// Methods to read documents. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.ReadClassName} Read => this.read ??= new( {databasePlan.DbClassNameArgument}: this.{databasePlan.DbClassName}, partitionKey: this.PartitionKey); {databasePlan.Namespace}.{partitionPlan.ReadOrThrowClassName}? readOrThrow; /// <summary> /// Methods to read documents, or throw DbConflictException is they are not found. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.ReadOrThrowClassName} ReadOrThrow => this.readOrThrow ??= new( {databasePlan.DbClassNameArgument}: this.{databasePlan.DbClassName}, partitionKey: this.PartitionKey); {databasePlan.Namespace}.{partitionPlan.CreateClassName}? create; /// <summary> /// Methods to create documents. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.CreateClassName} Create => this.create ??= new(this); {databasePlan.Namespace}.{partitionPlan.ReadOrCreateClassName}? readOrCreate; /// <summary> /// Methods to read documents, or create them if they did not yet exist. /// </summary> public virtual {databasePlan.Namespace}.{partitionPlan.ReadOrCreateClassName} ReadOrCreate => this.readOrCreate ??= new(this); {ReadMany(databasePlan, partitionPlan)} {CreateOrReplace(databasePlan, partitionPlan)} {string.Concat(partitionPlan.Documents.Select(x => Create(partitionPlan, x)))} {string.Concat(partitionPlan.Documents.Select(x => CreateOrReplace(partitionPlan, x)))} {string.Concat(partitionPlan.Documents.Select(x => ReadOrCreate(partitionPlan, x)))} {string.Concat(partitionPlan.Documents.Select(ReplaceIfMutable))} {string.Concat(partitionPlan.Documents.Select(DeleteIfTransient))} }} "; outputModel.Context.AddSource($"partition_{partitionPlan.ClassName}.cs", s); BatchWriter.Write(outputModel, databasePlan, partitionPlan); CreateWriter.Write(outputModel, databasePlan, partitionPlan); ReadOrCreateWriter.Write(outputModel, databasePlan, partitionPlan); CreateOrReplaceWriter.Write(outputModel, databasePlan, partitionPlan); ReadWriter.Write(outputModel, databasePlan, partitionPlan); ReadOrThrowWriter.Write(outputModel, databasePlan, partitionPlan); ReadManyWriter.Write(outputModel, databasePlan, partitionPlan); QueryBuilderWriter.Write(outputModel, databasePlan, partitionPlan); QueryWriter.Write(outputModel, databasePlan, partitionPlan); }
static string Partition(DatabasePlan databasePlan, PartitionPlan partitionPlan) => $@" public virtual {databasePlan.Namespace}.{partitionPlan.ClassName} {partitionPlan.Name}({partitionPlan.GetPkPlan.AsInputParameters()}) => new({new[] { CreateClassParameter(databasePlan), CreateKeyParameter(partitionPlan) }.JoinNonEmpty()}); ";
static void AddPartitionDefinition(OutputModel outputModel, DatabasePlan databasePlan, MethodModel methodModel, string name) { if (databasePlan.PartitionPlansByName.TryGetValue(name, out var partitionPlan)) { if (!SymbolEqualityComparer.Default.Equals(partitionPlan.GetPkModel.MethodSymbol, methodModel.MethodSymbol)) { outputModel.Report(Diagnostics.Errors.PartitionAlreadyDefined, methodModel.MethodSymbol); } } else { partitionPlan = new PartitionPlan { Name = name, PluralName = name.Pluralize(), ClassName = name.WithSuffix(Suffixes.Partition), BatchHandlersClassName = name.WithSuffix(Suffixes.BatchHandlers), CreateOrReplaceClassName = name.WithSuffix(Suffixes.CreateOrReplace), ReadClassName = name.WithSuffix(Suffixes.Read), ReadOrThrowClassName = name.WithSuffix(Suffixes.ReadOrThrow), ReadManyClassName = name.WithSuffix(Suffixes.ReadMany), ReadUnionsClassName = name.WithSuffix(Suffixes.ReadUnions), ReadOrThrowUnionsClassName = name.WithSuffix(Suffixes.ReadOrThrowUnions), ReadManyUnionsClassName = name.WithSuffix(Suffixes.ReadManyUnions), QueryBuilderClassName = name.WithSuffix(Suffixes.QueryBuilder), QueryBuilderUnionsClassName = name.WithSuffix(Suffixes.QueryBuilderUnions), QueryClassName = name.WithSuffix(Suffixes.Query), QueryUnionsClassName = name.WithSuffix(Suffixes.QueryUnions), ReadOrCreateClassName = name.WithSuffix(Suffixes.ReadOrCreate), CreateClassName = name.WithSuffix(Suffixes.Create), BatchClassName = name.WithSuffix(Suffixes.Batch), GetPkModel = methodModel }; partitionPlan.BatchHandlersClassNameArgument = partitionPlan.BatchHandlersClassName.ToArgumentName(); partitionPlan.ClassNameArgument = partitionPlan.ClassName.ToArgumentName(); databasePlan.PartitionPlansByName[name] = partitionPlan; partitionPlan.QueryBuilderClassNameArgument = partitionPlan.QueryBuilderClassName.ToArgumentName(); outputModel.ValidateNames( methodModel.MethodSymbol, partitionPlan.Name, partitionPlan.PluralName, partitionPlan.ClassName, partitionPlan.BatchHandlersClassName, partitionPlan.CreateOrReplaceClassName, partitionPlan.ReadClassName, partitionPlan.ReadOrThrowClassName, partitionPlan.ReadManyClassName, partitionPlan.QueryBuilderClassName, partitionPlan.QueryClassName, partitionPlan.ReadOrCreateClassName, partitionPlan.CreateClassName, partitionPlan.BatchClassName, partitionPlan.QueryBuilderUnionsClassName, partitionPlan.QueryUnionsClassName, partitionPlan.ReadManyUnionsClassName, partitionPlan.ReadOrThrowUnionsClassName, partitionPlan.ReadUnionsClassName); outputModel.ValidateIdentifiers( methodModel.MethodSymbol, partitionPlan.ClassNameArgument, partitionPlan.BatchHandlersClassNameArgument, partitionPlan.QueryBuilderClassNameArgument); } }
static string CreateKeyParameter(PartitionPlan partitionPlan) => partitionPlan.GetPkPlan.Arguments.Count == 0 ? "" : $@" pkData: new() {{ {partitionPlan.GetPkPlan.AsSettersFromParameters()} }}";