Use ObjectId.GenerateNewId() or leave MongoDB to create one? - c#

In C# I can use the ObjectId.GenerateNewId() to generate ObjectId values.
Most of the time when I insert a document in a collection I do not set the value of _id fields. MongoDB creates the ObjectId values automatically.
I would like to ask if it is safe to set it manually by using the ObjectId.GenerateNewId() method.

When you insert a new mongodb document the son driver check if exist a property with the BsonId AttributeClass. If exist and is null it create a new ObjectId, if doesn't exist during the document insertion mongodb will generate e new ObjectId.
Sometimes users encounter problem with "only zero" ObjectId, for this reason my suggestion is to use a combination of BsonID attribute class and ObjectId.GenerateNewId so you are sure that that property will not have weird behaviour.
e.g.
public class SomeClass {
[BsonId]
public ObjectId MySuperId { get; set; }
public SomeClass() {
this.MySuperId = ObjectId.GenerateNewId();
}
}

Related

How to store a C# Complex struct in SQL Server

I have a Complex number in a C# class
public class DataToStore
{
[Key]
public int Id { get; set; }
public System.Numerics.Complex ComplexValue { get; set; }
}
I need to store this data in a SQL Server database, but I don't know what the equivalent type is in SQL.
For now, I'm trying to generate the database schema using Entity Framework Core, but when creating the migration EF is complaining that it can't map this type:
The property 'DataToStore.ComplexValue' could not be mapped because it
is of type 'Complex', which is not a supported primitive type or a
valid entity type. Either explicitly map this property, or ignore it
using the '[NotMapped]' attribute or by using
'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Which makes sense, but I don't know how this data can be represented in SQL.
I'm not specifically tied to using Entity Framework - I'm only using this as a way to quickly mock up the database - so if this is something that can be done manually in SQL but not EF, then that's absolutely fine. My aim here is to get this data in the database and be able to get it out again in exactly the same format.
What's the best way of storing this type of data in SQL? Or does this value need converting to another type so that it can be stored?
You would need multiple database fields to store its values. If you want to store the Real and Imaginary values, you could have a field for each of them.
You would need to update your model so that EF does not try to save ComplexValue. You should have properties for the real and imaginary values and use them to make your complex value.
public class DataToStore
{
[Key]
public int Id { get; set; }
[NotMapped]
public System.Numerics.Complex ComplexValue
{
get
{
return new Complex(Real, Imaginary)
}
set
{
Real = value.Real;
Imaginary = value.Imaginary;
}
}
public double Real { get; set; }
public double Imaginary { get; set; }
}

What is best approach for data inheritation in elasticsearch?

I have an parent class and two child like these:
public class Parent {
public Guid Id { get; set; }
public string Name { get; set; }
}
public class FirstChild {
public string IdentityCode { get; set; }
}
public class OtherChild {
public string RegistrationCode { get; set; }
}
There is a question: Is it a good approach to store these two inherited classes in the same Index inside ElasticSearch?
I see there is a _type property that is added to my docs after they are stored in DB but it has always "doc" value.
I test this code to fill it but it seems it is not working this way.
await ElasticClient.IndexAsync<FirstChild>(child, m => m.Index(IndexName));
And Also, I found this question on SO for retrieving my entries from DB but it is outdated and the API is changed and no more accessible.
I want to know if it is a good approach to store sibling data in the same index how can I do this properly.
As of ES 6.0, it is not possible anymore to store multiple types inside the same index, i.e. the _type field you're referring to will always be either doc or _doc. In ES 8.0, the _type field will be removed altogether.
However, if it makes sense for your use case, you can still decide to store several types inside a single index using a custom type field that is present in your document.
You should strive to only store in the same index data that share the same (or very similar) mapping, which doesn't seem to be the case for Parent, FirstChild and SecondChild, but if you add a public string type property to your classes you can still do it.

Update all Fields passed in object without using replace

I'm writing a wrapper around certain functions of mongodb to enforce certain buisiness policies (such as having a last modified date, a document version &c). These extra fields will not appear in the model and will be irrelevant and transparent to the person implementing against this library. This library will be generic.
Therefore using replaceOne is out of the question.
What I would like is some way of passing all fields in a person passed object to the Update builder - so I can use .Set/.Inc accordingly to add the other fields.
An example to demonstrate what I want is below:
public static async Task UpdatePerson(string name, Person person)
{
var client = new MongoClient("mongodb://localhost:27017");
IMongoDatabase db = client.GetDatabase("test");
IMongoCollection<Person> collection = db.GetCollection<Person>("people");
var query = Builders<Person>.Filter
.Eq("name", name);
var update = Builders<Person>.Update
//Something here - how do I pass my person's properties?
.Set("lastModified", DateTime.Now)
.Inc("version",1);
await collection.UpdateOneAsync(query, update );
}
//--
//In real life this'll work for other types, this is for demonstration only
public class Person
{
public string name {get;set;}
public string surname {get;set;}
}
So how can I go about this, without, for instance, looping through properties using Reflection?
Not sure if you are able to do this but the Mongodb Driver provides something called [BsonExtraElements].
public class Person
{
public string name {get;set;}
public string surname {get;set;}
[BsonExtraElements]
public Dictionary<string,object> AdditionalFields { get; set; }
}
What will happen is that anything that cant be serialized to the model will be filled into that dictionary, no matter the type. You can add to it as well and remove.
This will add no additional overhead to your database, The only downside to this is that querying this dictionary is somewhat not a great experience as you may need to cast specific keys to their relevant expected types.
If this is not viable I suggest the BSON approach recommended by Simon.

Serializing Guid to MongoDB as a BsonString and not BinData?

I have a class that has the following property
[BsonId]
public Guid EventId { get; private set; }
I would like to serialize(and deserialize) the class that has this property to MongoDB with the use of the ToBsonDocument method. Though through the use of the default serializer the resulting BSON type for the _id field is
"_id" : BinDate(3, "wX9ZnP0ApEWF0d5aXLgiUA==")
I would like it to be stored as a BsonString. I plan to create a custom SerializerBase<> extension class in the future to properly deserialize the string back to a Guid I just have not gotten there yet.
I know that I could simply change my property to be a String
[BsonId]
public String EventId { get; private set; }
But I would like to keep it as a Guid. I think to solve my problem I need to make use of a serialization tag but I am not sure which one, any ideas?
Actually I somehow managed to find my answer right after I posted this question.
By making use of the tag BsonRepresentation I can specify the BSON data type that I would like MongoDB to use when serializing the data.
[BsonRepresentation(BsonType.String)]

C# + MongoDB - ObjectId without using MongoDB DataTypes/Attributes

Using MongoDB as my data store makes me to have ObjectID type as primary key by Default. It also can be changed by using Guid with [BsonId] attribute. Which is also defined in MongoDB C# Driver library. I would like to have my Entities independent from Data layer.
Can I just use name Id for the property to identify primary key? What else I can try?
You can use BsonClassMap instead of using attributes to keep your classes "clean".
// 'clean' entity with no mongo attributes
public class MyClass
{
public Guid Id { get; set; }
}
// mappings in data layer
BsonClassMap.RegisterClassMap<MyClass>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Id).SetIdGenerator(CombGuidGenerator.Instance);
});
OPTION 1: Stick with BsonId and use the Facade Pattern
The [BsonId] property is what you'd use to indicate that the _id property should be linked to a specific property. There isn't a way around that (short of ignoring _id entirely in your crud operations which seems like a bad idea).
So, if you want to separate your "entity" object from your "data layer" then just use a poco class.
-- Use a poco class as a substitute for a record. That class is only for data storage: a quick way to get data in/out of mongo, and a great alternative to working with bson documents.
-- Use a facade on top of that poco class for your entity layer. I don't find it useful to re-invent the wheel, so I typically ask our devs have the entity interface inherit the data-layer (poco) interface, but you can do it however you'd like
Breaking up a sample MyObject class
IMyObjectRecord (declared at the dal and contains only properties and mongo-specific attributes)
IMyObject:IMyObjectRecord (declared at the entity level and may include added properties and methods)
MyObjectRecord:IMyObjectRecord (declared inside the dal, contains mongo-specific attributes. Could be declared internal if you wanted to be really strict about separation).
MyObject:IMyObject (could be, for example, a facade on top of the IMyObjectRecord class you pull from the dal).
Now - you get all the benefits of the facade, and you have a hard-coded link between the properties BUT, you get to keep Bson attributes contained in your dal.
OK, fine. But I really really really HATE that answer.
Yeah. I can accept that. OK, so how about a Convention Pack? If you ABSOLUTELY PROMISE that you'll call your Id's "Id" and you SWEAR that you'll type them as strings (or -- use some other convention that is easy to identify), then we could just use a convention pack like the one I stole from here
namespace ConsoleApp {
class Program {
private class Foo {
// Look Ma! No attributes!
public string Id { get; set; }
public string OtherProperty { get; set; }
}
static void Main(string[] args) {
//you would typically do this in the singleton routine you use
//to create your dbClient, so you only do it the one time.
var pack = new ConventionPack();
pack.Add(new StringObjectIdConvention());
ConventionRegistry.Register("MyConventions", pack, _ => true);
// Note that we registered that before creating our client...
var client = new MongoClient();
//now, use that client to create collections
var testDb = client.GetDatabase("test");
var fooCol = testDb.GetCollection<Foo>("foo");
fooCol.InsertOne(new Foo() { OtherProperty = "Testing", Id="TEST" });
var foundFoo = fooCol.Find(x => x.OtherProperty == "Testing").ToList()[0];
Console.WriteLine("foundFooId: " + foundFoo.Id);
}
//obviously, this belongs in that singleton namespace where
//you're getting your db client.
private class StringObjectIdConvention : ConventionBase, IPostProcessingConvention {
public void PostProcess(BsonClassMap classMap) {
var idMap = classMap.IdMemberMap;
if (idMap != null && idMap.MemberName == "Id" && idMap.MemberType == typeof(string)) {
idMap.SetIdGenerator(new StringObjectIdGenerator());
}
}
}
}
}
What's a Convention Pack
It's a little set of mongo "rules" that get applied during serialize/deserialize. You register it once (when you setup your engine). In this case, the sample pack is telling mongo "if you see a field called 'Id', then save it as a string to _id, please."
These can get really complex and fun. I'd dig into convention packs if you really really really hate the other approach. It's a good way to force all your mongo "attribute driven" logic into one self-contained location.
I have stumbled on the same problem myself, and I didn't want to have mongo attributes inside my classes.
I have created a small wrapper example to show how I save and find elements without having an Id property on the data classes of my business logic.
The wrapper class:
public static class Extensions
{
public static T Unwrap<T>(this MongoObject<T> t)
{
return t.Element;
}
}
public class MongoObject<T>
{
[BsonId]
private ObjectId _objectId;
public T Element { get; }
public MongoObject(T element)
{
Element = element;
_objectId = new ObjectId();
}
}
I have also added an extension method to easily unwrap.
Saving an element is simple
public void Save<T>(T t)
{
_collection.InsertOne(new MongoObject<T>(t));
}
To find an element we can do a linq-like query:
Say we have a data class:
public class Person
{
public string Name { get; set; }
}
then we can find such an element by
public Person FindPersonByName(string name)
{
return _collection.AsQueryable().FirstOrDefault(
personObject => personObject.Element.Name == name).Unwrap();
}
We can also generalize by making MongoObject implement IQueryable<T> and this would make the use of the wrapper even more convenient
If i understand correctly. You want to put your entity to other layer without attribute.
I think you can try this
public object Id { get; set; }
after that you can put your Id which is coming from mongodb without attribute

Categories