jkratz55
jkratz55

Reputation: 881

.NET Core Cosmos Private Setter for ID Not Populating

I'm newer to .NET Core so excuse me if this question is not well suited for Stackoverdlow. I am using CosmosDB with .NET Core 2.1. I have a simple class I am persisting to a collection below.

public class Customer
{
    public string Id { get; private set; }
    public string FirstName { get; private set; }
    public string LastName { get; private set; }

    public Customer(string firstName, string lastName) {

        if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
        {
            throw new ArgumentException("First and Last name are required");
        }

        this.FirstName = firstName;
        this.LastName = lastName;
    }
}

Notice the private set because well the Id is autogenerated by the database and should never be set by the caller.

When I save records and retrieve them the Id property is not populated. However if I change the setter to public it works just fine. This is a really simple example but ideally I should be able to make the Id setter private as it should be immutable outside the class. I've used libraries like Hibernate in Java in the past where this worked fine because the field was set via reflection.

Is there a way .NET Core and CosmosDB can handle private setter? I can see where this would an issue on a domain like Order when trying to implement a OOP/DDD approach.

public class Order
{
    public int Id { get; private set; }
    public IList<LineItem> LineItems { get; private set; }

    public void AddLineItem(LineItem lineItem) {
        // do some business logic, encapsulatng the line items
        // IE don't just let the caller have free reign 
    }
}

public class LineItem 
{
    public string SKU { get; set; }
    public int Qty { get; set; }
    public decimal PricePerUnit { get; set; }
}

Upvotes: 2

Views: 1250

Answers (1)

bit
bit

Reputation: 4487

Since CosmosDb has the pre-defined property id, you need the JSON serializer to bind to it, and since this is case sensitive, here's the attribute that allows you to do that:

public class Customer
{
    [JsonProperty("id")]
    public string Id { get; private set; }
    // other members
}

Personally, along with that, I prefer to add another property to store any unmapped properties

/// <summary>
/// Any unmapped properties from the JSON data deserializes into this property.
/// </summary>
[JsonExtensionData]
public IDictionary<string, JToken> UnmappedData { get; set; }

Thus, at least while debugging I become aware of any property which I might have missed out due to case sensitivity, misspelling or etc.

Effectively, my Base class for CosmosDb model looks like:

/// <summary>
/// Implements auto generated Id property for CosmosDb Model
/// </summary>
public abstract class BaseModel
{
    /// <summary>
    /// PK for this model. (apart from the ResourceId which is internally generated by CosmoDb)
    /// If the user does not set this, the SDK will set this automatically to a GUID.
    /// </summary>
    [JsonProperty(PropertyName = "id")]
    public virtual string Id { get; set; }

    /// <summary>
    /// Any unmapped properties from the JSON data deserializes into this property.
    /// </summary>
    [JsonExtensionData]
    public IDictionary<string, JToken> UnmappedData { get; set; }
}

Upvotes: 4

Related Questions