Coder Social home page Coder Social logo

Comments (4)

RobThree avatar RobThree commented on June 12, 2024

Hi Marco,

Okay, this one's a bit tough to explain but I'll try. I must admit you had me convinced at first this was a bug, but, now, I do not agree (totally) but I will admit it's a bit sketchy...

I'll try to work this out with an example:

Suppose you implemented a class as IEntity<int> instead; this way it's pretty clear that when you implement IEntity (instead of derive from Entity) you're pretty much responsible yourself for generating (unique) Id's upon inserting items. So you set MyObject.Id = 12345 on creation (or just before insertion) of the object and can then call MyRepo.Delete(MyObject.Id);. Everything's fine here.

Now you implement IEntity<string>. Because the Id is a string Mongo will generate an Id for you (specifically: an ObjectId) as you indicated with the BsonRepresentation attribute. However, upon deletion a string is passed in and Mongo goes off searching for a string which it will never find; it needs to look for an ObjectId. Your proposed fix fixes the specific case where the Id happens to also be an ObjectId. But it will fail if I created an entity implementing IEntity<string> on which I'd like to assign, say, a GUID (Guid.NewGuid().ToString("N")) for the Id property instead of an ObjectId. The IEntity interface implicitly means you have to handle assigning Id's yourself and you loose the ObjectId (MongoRepository tries to abstract as much Mongo-specific stuff away anyway to be as loosely-coupled as possible; the attributes I never recommend although they can come in handy admittedly).

I guess the best solution for you would be to drop the BsonRepresentation attribute and handle assigning Id's, possibly in the constructor or in your derived repository's Add(...) method (using ObjectId.GenerateNewId().ToString()), to make sure the value isn't persisted in MongoDB as an ObjectId but as actual string. This way, the Delete() method will also be able to find the matching object by it's (persisted-as-string) Id.

I admit it's a bit hairy but I can't break backwards compatibility (or at least: strive to do so as little as possible). I could, alternatively, check in the else clause of the Delete() method if the string 'looks like an ObjectId' (using ObjectId.TryParse()) and pass the string as ObjectId to Mongo or else just as a normal string. But this kind of "autodetection" usually causes more harm than it does good and has the potential to also create edge-cases/bugs that are hard to find/reproduce since you expect a string to be used as id which is then 'automagically' converted to an ObjectId without you knowing (essentially creating a similar issue as you have now).

Your proposed .GetInterfaces().Contains(...) solution is also, potentially, a pretty 'expensive' one, depending on the number of interfaces an object implements. I'm pretty sure this could be implemented like, for example, something along the lines of "if (T is IEntity)" or "typeof(IEntity).IsAssignableFrom(typeof(T))" (but then their 'generic variations') but I don't have an development environment, nor the time, at hand currently to work out what it would look like exactly. But we can't go on IEntity<string> as I explained earlier since the string that Id is may or may not be an ObjectId. I'm afraid we'd have to use some ugly reflection or other way to find out if the attribute is set on the Id property of T which, again, will probably be a costly operation and I'm not sure if we'd ever be able to get it rock solid. I'm just posting this as a hint for people wanting to think along with me/us.

I am, as always, open for suggestions and discussion. Let me know what you think! Meanwhile, I'll try to see if I can come up with a clean, satisfactory, solution that works in all cases. Maybe it turns out to be stupendously simple (I've been there before) and I (or someone) comes up with a beautifully elegant solution...

At least I hope I explained the issue and my problem(s) in (trying to) solving this.

from mongorepository.

fabersheep avatar fabersheep commented on June 12, 2024

Hi, thank you for the response.

I kind of understood what you've explained, even if this is my first mongodb project, so I'm new to this world.

As I don't have a solution I will try to explain you why I've implemented the IEntity instead of subclass the Entity class: I wanted some custom fields that comes in handy like the CreationDate property (obtained from the handy objectId) and an Init method that I call from my ultra generic webapi controller.

Obviously I wouldn't pollute the Entity class or ask you to (even if perhaps those fields could be useful to everybody I guess and shouldn't generate so much overhead), so at first I subclassed the entity with a custom class called BaseObject : Entity where I added my fields. Therefore all my business objects inherited from BaseObject.

Everything in the project started to fail at runtime, and after a bit of debugging I found out that MongoRepository was writing this objects into the BaseObject collection instead of the proper collection that matched the class name: I had a BaseObject collection with subjects, orders, addresses, etc. all mixed.

So I reverted it and done a "intelligent lending" (aka copypaste) of your Entity class, that led me to discover this issue.. and here I am :)

Regarding the GetInterfaces().Contains vs IsAssignableFrom I thought of that issue too but I read somewhere on stackoverflow that the IsAssignableFrom isn't so much reliable (but I mildly assimilated the info without a deeper understanding).

So, getting to the point, I can:

  1. Leave this as is and implement my own modification of your library every release (umh)
  2. Lure you into adding the CreationDate property and the Init() method to the Entity class (ummmmmh)
  3. Revert the BaseObject to be a subclass of entity and somehow fix the collection name resolver bug (if still there... it's been a while). Perhaps decorating the class with the attribute of the collection name should do the trick but I'm lazy :)

thanks for your time
Marco

from mongorepository.

tegeek avatar tegeek commented on June 12, 2024

I don't think you are doing this in a right way.

If you look inside MongoRepositoryTests folder you can see there is an implementation of Custom id property.

[CollectionName("MyTestCollection")]
public class CustomIDEntityCustomCollection : CustomIDEntity { }

You can have any structure for your entities (inheritance etc.) as far as you follow the directions. The scenario you'are describing are almost all covered in Tests.

from mongorepository.

fabersheep avatar fabersheep commented on June 12, 2024

Yes, as I said I will try decorating the class with the collectionname attribute and that should do the trick.

I would have expected, as long as I was only subclassing the Entity class and not making a custom Id entity, that the collection autonaming mechanism would work without it, but it's no big deal using it.

Regarding the oringial issue, perhaps the "autodetection" mechanism could be implemented in the else clause of the Delete method just to throw an Excepion explaining the issue so the developer is aware that something is wrong, because right now it just silently fail.

Best regards,
Marco

from mongorepository.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.