Chad Wilkin
Chad Wilkin

Reputation: 311

Easier way to store list of id's in a parent document using RavenDB

If I have a parent document that can have multiple children such as a Store can have multiple Products is there any easier way to store a list of Product.Id in the Store document?

Currently I am just storing the Product objects first and then looping through them to get the Product.Id for the Store.ProductIds property.

Store

class Store
{
    public string Id { get; set; }
    public string Name { get; set; }
    public IList<string> ProductIds { get; set; }
    [JsonIgnore]        
    public IList<Product> Products { get; set; }
}

Product

class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
}

Current Working Save Method

var store = new Store()
{
     Name = "Walmart",
     Products = new List<Product>
                    {
                        new Product {Name = "Ball"},
                        new Product {Name = "Bat"}
                    }
};

using (var session = DocumentStore.OpenSession())
{
    foreach (var product in store.Products)
    {
        session.Store(product);
    }

    session.SaveChanges();

    var list = new List<string>();

    foreach (var product in store.Products)
    {
        list.Add(product.Id);
    }

    store.ProductIds = list;

    session.Store(store);
    session.SaveChanges();
}

Upvotes: 1

Views: 168

Answers (1)

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241555

To answer your specific question - there are two things you can simplify with the code:

  • You can eliminate the first session.SaveChanges(); The product ids will be created when you call .Store() on the product.

  • You can gather the product ids with some linq to collapse it to one line:

    store.ProductIds = store.Products.Select(x=> x.Id).ToList();

You still have the same general approach though - it's just simplifying the code a bit.

This approach will work, but do realize that you are just putting the Products in the Store for convenience. [JsonIgnore] is ok here, but it only helps with serialization - not deserialization. In other words, when loading a store, only the ProductIds list will be populated. You would have to load them separately (possibly using .Include())

Personally, I would take the Products list out of the Store object altogether. Other relational products like Entity Framework and NHibernate use virtual members and proxy classes for this purpose, but it has little meaning in RavenDB. A consumer of your class won't know that the property is ignored, so they might misunderstand its usage. When I see the Products property, my expectation is that each Product is fully embedded in the document - in which case you wouldn't need the separate ProductIds list. Having them both with one ignored just causes confusion.

Another argument against your proposed design would be that it implicitly makes every product in every store unique. This is because you are creating the products with the store, and then adding each one separately. If it is indeed the case that all products are unique (not just "ball", but "this specific ball"), then you could just embed the product without the [JsonIgnore] or the ProductIds list, and there would be no need for Product to exist as a separate document. In the more likely scenario that products are not unique (multiple stores can sell bats and balls), then you have two options:

class Store
{
    public string Id { get; set; }
    public string Name { get; set; }
    public IList<string> ProductIds { get; set; }
}

class Store
{
    public string Id { get; set; }
    public string Name { get; set; }
    public IList<Product> Products { get; set; }
}

Product would still be in its own document with either case - the second scenario would be used as a denormalized reference so you can get at the product name without loading the product. This is acceptable, but if product names can change, then you have lots of patching to do.

Sorry if there's no clean "do it this way" answer. Raven has lots of options, sometimes one way or another works better depending on all the different ways you might use it. Me personally - I would just keep the list of ProductIds. You can always index the related document to pull in the product name for querying purposes.

Upvotes: 2

Related Questions