This project has moved. For the latest updates, please go here.

Null ObjectId on List<> items in Classes

Oct 24, 2013 at 9:48 PM
Hello, quick question. Why does the MongoReposotory not serialize the Id of List<> items in an Entity?
In the Step-by-step Customer\Product example, even if Product is derived from Entity, Id is always null. This means I can never do something like:
customerrepo.DeleteProduct(jerry.Id, product1.Id);

So, in the Step-by-step example, how can I easily remove a product from a customer without getting all the Products for a Customer or knowing everything about the Product I want to remove?

Especially if the customer in this example has tens of thousands of products?
If I am missing something obvious please let let me know.
Thanks,
John
Coordinator
Oct 24, 2013 at 10:01 PM
Edited Oct 24, 2013 at 10:10 PM
I'm afraid I need some relevant code; I just created a test-case and this works fine for me:
using MongoRepository;
using System;
using System.Linq;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var repo = new MongoRepository<Customer>();
            var robiii = new Customer()
            {
                Name = "Rob Janssen",
                Address = "SomeStreet 1234",
                Products = new List<Product>(
                    new[] {
                        new Product() { Id =1, Name="Hot sauce", Price=1.00m },
                        new Product() { Id= 2, Name="Spaghetti", Price = 2.00m },
                        new Product() { Id= 3, Name="Meatballs", Price = 3.00m },
                    })
            };
            repo.Add(robiii);

            var test = repo.GetById(robiii.Id);
        }
    }

    class Customer : Entity
    {
        public string Name { get; set; }
        public string Address { get; set; }

        public List<Product> Products { get; set; }
    }

    class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Decimal Price { get; set; }
    }
}
This results in:
{
  "_id" : ObjectId("52698a22fb27763c78abdd2c"),
  "Name" : "Rob Janssen",
  "Address" : "SomeStreet 1234",
  "Products" : [{
      "_id" : 1,
      "Name" : "Hot sauce",
      "Price" : "1.00"
    }, {
      "_id" : 2,
      "Name" : "Spaghetti",
      "Price" : "2.00"
    }, {
      "_id" : 3,
      "Name" : "Meatballs",
      "Price" : "3.00"
    }]
}
As you can see, the Id's for products are stored fine (and test contains an exact copy with products and their Id's). This also works fine: var test = repo.Where(c => c.Products.Any(p => p.Id == 2));

The only thing I can think of right now is that your product's Id might not be accessible (maybe it's private)?
Oct 25, 2013 at 4:21 PM
Hello, thank you for the quick response!
Looking at your example, you are setting the Product.Id when you are creating new Products.
I want to have an BsonType.ObjectId automatically maintained just like on the Customer record.
I guess I am looking for the framework to manage the GUID\Identity for me much like an identity and not care about the value. Is this possible? It seems it should be. I realize I can handle this myself but I don't want to worry about when I need to manage the Id and when I don't. I just want to Add() it to the repository and not worry about if the Id is atomic, unique, etc. Please let me know if I am out of my mind here...

I modified your code to show you basically what I am doing in my code.

using MongoRepository;
using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var repo = new MongoRepository<Customer>();
var robiii = new Customer()
{
Name = "Rob Janssen",
Address = "SomeStreet 1234",
Products = new List<Product>(
new[] {
new Product() { Name="Hot sauce", Price=1.00m },
new Product() { Name="Spaghetti", Price = 2.00m },
new Product() { Name="Meatballs", Price = 3.00m },
})
};
repo.Add(robiii);

var test = repo.GetById(robiii.Id);
}
}

[Serializable]
[CollectionName("Customer")]
class Customer : BaseEntity
{
public string Name { get; set; }
public string Address { get; set; }

public List<Product> Products { get; set; }
}

[Serializable]
[CollectionName("Customer")]
class Product : BaseEntity
{
public string Name { get; set; }
public Decimal Price { get; set; }
}

public class BaseEntity : Entity
{
public BaseEntity()
{
CreatedDateTime = DateTime.Now;
}
public DateTime CreatedDateTime { get; set; }
}
}

This results in:
{
  "_id" : ObjectId("52698a22fb27763c78abdd2c"),
  "Name" : "Rob Janssen",
  "Address" : "SomeStreet 1234",
  "Products" : [{
      "_id" : null,
      "Name" : "Hot sauce",
      "Price" : "1.00"
    }, {
      "_id" : null,
      "Name" : "Spaghetti",
      "Price" : "2.00"
    }, {
      "_id" : null,
      "Name" : "Meatballs",
      "Price" : "3.00"
    }]



Coordinator
Oct 25, 2013 at 5:17 PM
Edited Oct 25, 2013 at 5:22 PM
jstorch wrote:
Hello, thank you for the quick response! Looking at your example, you are setting the Product.Id when you are creating new Products. I want to have an BsonType.ObjectId automatically maintained just like on the Customer record.
If you're using these Id's as a "foreign key" then the entities should be in their own collection which will then, just like Customer, give the products their own unique Id's. Then you can expose a List<string> or something on the customer object where each string represents/refers to a product.
I guess I am looking for the framework to manage the GUID\Identity for me much like an identity and not care about the value.
Do you really need an Id? What if "spaghetti" is assigned to customer A and B? If they both refer to the same "spaghetti-instance", use the scenario I described above. If "spaghetti" is just a product assigned to customer A and there's another "spaghetti" assigned to customer B, you might want a simple array? That way the index can be considered as "id" and will guaranteed be unique (within that customer-instance that is).
I realize I can handle this myself
That is option three indeed ;)
but I don't want to worry about when I need to manage the Id and when I don't
I just want to Add() it to the repository and not worry about if the Id is atomic, unique, etc.
That is exactly why you stuff this kind of funcionality in the repository so that someone (you) using the repository doesn't have to worry about it.
Please let me know if I am out of my mind here...
You may be :P No, kidding aside, I just think we have different views/opinions on how this should be handled ;-)
Oct 25, 2013 at 6:55 PM
Hello and thanks again for the quick response.
I'll try to answer your questions. No, in this example, I don't think the Product.ID should be a foreign key, Customer1 has completely different Products than Customer2,

But lets say Customer1 has 100 products and a I have a page where a user can look at the Products and delete them id they want to.
What would be the best way to remove a single Product from Customer1's List<Product>?

This is really what I am trying to solve.
In my mind it would be calling something like "customerrepo.DeleteProduct(jerry.Id, product1.Id); "

What is the most efficient way of handling this?
I do I need to search through every one of Customer1 Products to find the one I want to delete?
Coordinator
Oct 25, 2013 at 7:08 PM
Edited Oct 25, 2013 at 7:10 PM
As said, I'd probably use it's index: To remove a specific product from the interface, all you need to know is the product's index (instead of Id) and do a

mycustomer.Products.RemoveAt(theindex).
Oct 25, 2013 at 8:20 PM
Index is retaliative to the number (and order) of items in the list, it does not identify any specific Product.

In my example, if two people are looking at same delete Product listing and user1 decides to delete the very first first Product (RemoveAt(0)) a second before user2 decides to delete the 50th Product (RemoveAt(49)), user2 just deleted the wrong product along with everyone else who's state doesn't match the server.

I guess I am just missing why any class inheriting from MongoRepository.Entiry does not have a valid MongoDB ObjectID.
If I didn't need a unique identifier for a class in a List as you suggest then I wouldn't reference MongoRepository.Entiry at all and there would be no Id property on the object in the fist place.
The fact that the property exists on the class and the field exists in the MongoDB collection on the server but it doesn't get wired up seems counter-intuitive.

I'm really not trying to be a pain here but I get the feeling I am close to receiving, "it doesn't work this way" or "you're just not getting it" ;-)
Thanks,
Jon
Coordinator
Oct 25, 2013 at 8:46 PM
Edited Oct 25, 2013 at 8:50 PM
jstorch wrote:
Index is retaliative to the number (and order) of items in the list, it does not identify any specific Product.
Ah, I see what you mean now. In that case you could consider passing a hashcode along with the Id or, generating Id's on the fly.
I guess I am just missing why any class inheriting from MongoRepository.Entiry does not have a valid MongoDB ObjectID.
Well, the Product is not the entity, the customer is (in this example) IMHO, you could dispute that.
If I didn't need a unique identifier for a class in a List as you suggest then I wouldn't reference MongoRepository.Entiry at all and there would be no Id property on the object in the fist place.
That's right :P
The fact that the property exists on the class and the field exists in the MongoDB collection on the server but it doesn't get wired up seems counter-intuitive.
Consider this: when saving an entity MongoRepository would have to ("deep"-)scan the entire object("tree") to see if any of the objects it contains has a property that could be considered an Id (so you'd have to decorate these properties with some attribute(s) accordingly). Mongo only generates Id's for documents (customer in this example). You can do that yourself and this doesn't force a specific implementation on the way you implement "id generation" (be it autonumbering, guid, timestamp, whathaveyou...). You could use DI or something to set an "Id generator" etc. etc. but this would overly complicate MongoRepository and still enforce "some" specific way of using it. Implementing it yourself gives you all the freedom and the rest of us none of the drawbacks :-)
I'm really not trying to be a pain here but I get the feeling I am close to receiving, "it doesn't work this way" or "you're just not getting it" ;-)
I guess it doesn't work that way :P
Thanks,
Jon
You're welcome. Suggestions and/or improvements are always welcome; if you have a great idea feel free to post it!