Reputation: 311
If I have a parent document that can have multiple children such as a Store
can have multiple Product
s 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
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