![]() |
VOOZH | about |
dotnet add package Cosmogenesis.Core --version 1.7.0
NuGet\Install-Package Cosmogenesis.Core -Version 1.7.0
<PackageReference Include="Cosmogenesis.Core" Version="1.7.0" />
<PackageVersion Include="Cosmogenesis.Core" Version="1.7.0" />Directory.Packages.props
<PackageReference Include="Cosmogenesis.Core" />Project file
paket add Cosmogenesis.Core --version 1.7.0
#r "nuget: Cosmogenesis.Core, 1.7.0"
#:package Cosmogenesis.Core@1.7.0
#addin nuget:?package=Cosmogenesis.Core&version=1.7.0Install as a Cake Addin
#tool nuget:?package=Cosmogenesis.Core&version=1.7.0Install as a Cake Tool
Cosmogenesis is a C# source generator for CosmosDB.
You define some documents, sprinkle in a couple [attribute]s, and Cosmogenesis spits out all the code to use it in CosmosDB.
A Database...
[Db] attributeA Partition...
[Partition] attributeGetPkA Document...
DbDocid property calculated using a static method you define named GetIdBy creating some documents and attributes, Cosmogenesis will create code handling:
Install both Cosmogenesis.Core and Cosmogenesis packages from Nuget.
Your CosmosDB container must have the following properties:
/pkMy partition key is larger than 100 bytesCreate a C# class library
Install the Cosmogenesis and Cosmogenesis.Core Nuget packages.
Create some documents and sprinkle in some attributes
[Db("DatabaseName", Namespace = "Optional.Namespace")] to define a database. If only 1 database exists (which is most convenient), then all documents by default go into it. Otherwise, [Db] must appear on every document class.
[Partition("PartitionName")] to define a partition. Attaching this to an abstract base class is often convenient, so all documents can derive from the base class and do not need to specify [Partition].
[Mutable] marks a document which has properties that can change. Without it, Replace functions will not be generated.
[Transient] marks a document which can be deleted. Without it, Delete functions will not be generated. Optionally sets a document's ttl to auto-expire.
[UseDefault] can be used on properties that don't always need initialization. Methods like Create generate parameters with default values for you.
[PartitionDefinition] can be used on a static class containing static methods, each of which will define a new partition. Instead of the implicit GetPk method, the static methods in this class are used to generate the partition keys.
[DocType("SomeType")] can be used to control the Type property which exists on all DbDoc documents. By default, a class named OrderDoc would have a Type value of Order, but you can override this with [DocType].
Define some documents (these are from Cosmogenesis.TestDb4):
[Partition("Accounts")]
public abstract class AccountDocBase : DbDoc
{
public static string GetPk(Guid accountId) => $"Account={accountId:N}";
public Guid AccountId { get; init; }
}
[Mutable]
public sealed class AccountInfoDoc : AccountDocBase
{
public static string GetId() => "Info";
public string Name { get; set; } = default!;
public bool IsEvil { get; set; }
public int MinionCount { get; set; }
}
[Transient]
[Mutable]
public sealed class PhoneNumberDoc : AccountDocBase
{
public static string GetId(string phoneNumberType) => $"PhoneNumberType={phoneNumberType}";
public string PhoneNumberType { get; init; } = default!;
public string PhoneNumber { get; set; } = default!;
public bool IsActive { get; set; }
}
[Partition("Orders")]
public sealed class OrderDoc : DbDoc
{
public static string GetPk(Guid accountId) => $"Orders={accountId:N}";
public static string GetId(string orderNumber) => $"Order={orderNumber}";
public Guid AccountId { get; init; }
public string OrderNumber { get; init; } = default!;
public class Item
{
public string ItemCode { get; init; } = default!;
public decimal UnitCost { get; init; }
public long Quantity { get; init; }
}
public Item[] Items { get; init; } = default!;
public List<string> Notes { get; init; } = default!;
public decimal TotalPrice { get; init; }
}
Now, in some other app, you can add a reference to the project you just created.
Here is some sample code (from Cosmogenesis.TestDb4.App) so you get a flavor of how it gets consumed:
var cosmosClient = new CosmosClient("a connection string");
var container = cosmosClient.GetDatabase("a database name").GetContainer("a container name");
var db = new EvilCorpDb(container); // using Evil.Corp.Database;
await db.ValidateContainerAsync();
var acctId = Guid.NewGuid();
// Create a single AccountInfo document
var accountInfo = await db
.Partition
.Accounts(accountId: acctId)
.Create
.AccountInfoAsync(name: "Bob", isEvil: true, minionCount: 4)
.ThrowOnConflict(); // using Cosmogenesis.Core;
// Update minion count and add two PhoneNumber documents, atomically in a batch
++accountInfo.MinionCount;
await db
.Partition
.Accounts(accountId: acctId) // We could have saved this value from before
.CreateBatch()
.CreatePhoneNumber(phoneNumberType: "Mobile", phoneNumber: "555-Evil", isActive: true)
.CreatePhoneNumber(phoneNumberType: "Land", phoneNumber: "556-Evil", isActive: true)
.Replace(accountInfo)
.ExecuteOrThrowAsync(); // Explode if the batch fails
// The accountInfo variable is now "stale" and must be reloaded if more operations on it are needed
// To avoid this, we could have used CreateBatchWithResults to get the new version of accountInfo
// Load any "Mobile" phone number for this account
var mobile = await db
.Partition
.Accounts(accountId: acctId)
.Read
.PhoneNumberAsync(phoneNumberType: "Mobile");
// And delete it if we found one
if (mobile is not null)
{
await db
.Partition
.Accounts(accountId: acctId)
.DeleteAsync(mobile)
.ThrowOnConflict();
}
// Create an order for the account
await db
.Partition
.Orders(accountId: acctId)
.Create
.OrderAsync(
accountId: acctId,
orderNumber: DateTime.Now.ToString(),
items:
[
new OrderDoc.Item { ItemCode = "abc", Quantity = 1, UnitCost = 1.99m },
new OrderDoc.Item { ItemCode = "def", Quantity = 100, UnitCost = 999 }
],
notes: ["This person is pretty evil", "Be careful!"],
totalPrice: 99901.99m)
.ThrowOnConflict();
// Load all "Land" phone numbers that are active for all accounts
var activeLandPhoneNumbers = await db
.CrossPartitionQuery
.PhoneNumbers(x => x.Where(p => p.IsActive && p.PhoneNumberType == "Land"))
.ToListAsync(); // Install nuget package System.Linq.Async for this
Here's what the OrderDoc looks like inside CosmosDB, which the code above created:
{
"AccountId": "7a8ca455-7346-4d85-ab01-28890e733b92",
"OrderNumber": "3/19/2021 1:44:13 PM",
"Items": [
{
"ItemCode": "abc",
"UnitCost": "1.99",
"Quantity": "1"
},
{
"ItemCode": "def",
"UnitCost": 999,
"Quantity": "100"
}
],
"Notes": [
"This person is pretty evil",
"Be careful!"
],
"TotalPrice": "99901.99",
"pk": "Orders=7a8ca45573464d85ab0128890e733b92",
"id": "Order=3_19_2021 1:44:13 PM",
"_etag": "\"9b00a55d-0000-0a00-0000-60550d1c0000\"",
"Type": "OrderDoc",
"CreationDate": "2021-03-19T20:44:13.1272959Z",
"_rid": "WWZSAPAwiFIEAAAAAAAAAA==",
"_self": "dbs/WWZSAA==/colls/WWZSAPAwiFI=/docs/WWZSAPAwiFIEAAAAAAAAAA==/",
"_attachments": "attachments/",
"_ts": 1616186652
}
USE NAMED PARAMETERS!!!
The source generators do not guarantee the same parameter ordering. Use named parameters to avoid nasty gotchas.
Example: Something(bool isOk, bool willDieImmediately); without named parameters would be called like Something(true, false). Without warning, the source generator might change it to Something(bool willDieImmediately, bool isOk);, in which case your parameters are backwards. Using Something(isOk: true, willDieImmediately: false) will always work without problems.
Don't rename document classes unless you really know what you're doing. Serializing depends on the Type property. The Type property is (by default) generated by the class name.
Don't rename document properties either. Remember that the fields you use are saved in a database in JSON documents, and if you start renaming properties in your class, the serializer won't pick up those properties when deserializing existing data.
Don't change the GetId or GetPk methods after they've been defined.
Cosmogenesis uses some JSON converters by default to support common scenarios and handle some edge cases.
These conversions should be kept in mind while constructing queries.
byte[] will be serialized into a hexadecimal string. Always lowercase, always an even-number string length (2 hex digits per byte).
long ulong decimal Int128 UInt128 BigInteger and BigFraction will be serialized into a string.
This avoids the large integer JSON data-loss problem and CosmosDB numeric type limitations.
But it also means these values are no longer natively sortable! "10" is less than "6", now that they're strings.
Note: float, double, and 32-bit integers (int, uint) are not converted - they are stored as JSON numbers and can be natively sorted by CosmosDB.
DateTime types are always stored in UTC time in ISO 8601 format with 7 digits of fractional seconds.
This avoids the CosmodDB date sorting problem due to minimized fractional digits. See: https://github.com/Azure/azure-cosmos-dotnet-v3/issues/1468
When deserializing, DateTime properties will always be deserialized into UTC, even if they were originally set using a local time.
DateOnly will be serialized to yyyy-MM-dd format.
Cosmogenesis uses the built-in [JsonStringEnumConverter] converter with default values.
IPAddress instances will be converted to and from string.
[JsonConverter] can be used as well.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 was computed. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.7.0 | 235 | 10/8/2025 |
| 1.6.2 | 193 | 10/3/2025 |
| 1.6.1 | 232 | 9/24/2025 |
| 1.6.0 | 304 | 9/19/2025 |
| 1.5.6 | 368 | 9/18/2025 |
| 1.5.5 | 236 | 9/11/2025 |
| 1.5.4 | 231 | 9/9/2025 |
| 1.5.3 | 233 | 9/9/2025 |
| 1.5.2 | 253 | 9/7/2025 |
| 1.5.1 | 251 | 9/7/2025 |
| 1.5.0 | 254 | 9/4/2025 |
| 1.4.0 | 247 | 9/1/2025 |
| 1.3.0 | 636 | 11/29/2022 |
| 1.2.3 | 693 | 4/27/2022 |
| 1.2.2 | 692 | 4/13/2022 |
| 1.2.1 | 706 | 4/5/2022 |
| 1.2.0 | 686 | 4/2/2022 |
| 1.1.0 | 682 | 3/27/2022 |
| 1.0.9 | 673 | 3/20/2022 |
| 1.0.8 | 679 | 3/20/2022 |