tags:

views:

28

answers:

1

I need to store some objects of this classes:

public class Category{
    public ObjectId Id {get;set;}
    public string Name {get;set;}
    public string Description {get;set;}
    public List<Product> Products {get;set;}
}

public class Product{
    public ObjectId Id {get;set;}
    public string Name {get;set;}
    public string Description {get;set;}
    public decimal Price {get;set;}
}

When I use NoRM and store a Category object with mongo.GetCollection().Insert(Category); I can see in mongo shell:

db.Category.find()
{ "_id" : ObjectId("82bbbf0179eae0141d020000"), "Name" : "Test products", "Descr
iption" : "This is test category", "Products" : [
        {
                "_id" : ObjectId("81bbbf0179eae0141d000000"),
                "Name" : "Product1",
                "Description" : "first product",
                "Price" : {

                }
        },
        {
                "_id" : ObjectId("82bbbf0179eae0141d010000"),
                "Name" : "Product2",
                "Description" : "second product",
                "Price" : {

                }
        }
] }

Can I store Category and Product objects in different collections and have just a reference to a Product within a Category record without changing the code of classes? (The way like NHibernate does)

A: 

No, you can't. At least not automatically.

SQL databases are best for storing normalized data across multiple tables. The relations between tables are defined by foreign key relations. NHibernate uses these foreign key relations to map objects to multiple tables.

MongoDB is geared towards storing documents. These documents are usually a denormalized representation of the data. The idea is to store related data in a single document instead of multiple tables. As a result of this philosophy MongoDB has no need for foreign key concepts or the ability to join collections.

In theory NoRM could support such functionality in the future, but this would go against the spirit of document databases. So it's unlikely that it will ever be supported.

Manual Solution

You can tell NoRM to skip the Products property when saving a category by applying the MongoIgnore attribute. Then store the products manually in a separate collection, together with a 'reference' to the category. You can auto-track the category for products by using a custom collection. The code would look something like this:

public class ProductCollection : Collection<Product>
{
  private Category owner;

  public ProductCollection(Category owner)
  {
    // Remember who 'owns' the products in this collection.
    this.owner = owner;
  }

  protected override void InsertItem(int index, Product item)
  {
    // Tell the product to which category it belongs.
    item.CategoryId = this.owner.Id;

    base.InsertItem(index, item);
  }

  // Override other methods using the same pattern.
}

public class Category
{
  public Category()
  {
    this.Id = ObjectId.NewObjectId();
    this.Products = new ProductCollection(this);
  }

  public ObjectId Id { get; set; }
  public string Name { get; set; }
  public string Description { get; set; }

  [MongoIgnore]
  public ProductCollection Products { get; private set; }
}

public class Product
{
  public ObjectId Id { get; set; }
  public string Name { get; set; }
  public string Description { get; set; }
  public decimal Price { get; set; }
  public ObjectId CategoryId { get; set; }
}

Now you can store categories in one collection and the products in another collection. The CategoryId property will indicate the category to which a product belongs.

Niels van der Rest
Thank you for full answer. I'll use this solution in my project.
Michael