internal LiteCollection(string name, BsonAutoId autoId, ILiteEngine engine, BsonMapper mapper) { _collection = name ?? mapper.ResolveCollectionName(typeof(T)); _engine = engine; _mapper = mapper; _includes = new List <BsonExpression>(); // if strong typed collection, get _id member mapped (if exists) if (typeof(T) == typeof(BsonDocument)) { _entity = null; _id = null; _autoId = autoId; } else { _entity = mapper.GetEntityMapper(typeof(T)); _id = _entity.Id; if (_id != null && _id.AutoId) { _autoId = _id.DataType == typeof(Int32) || _id.DataType == typeof(Int32?) ? BsonAutoId.Int32 : _id.DataType == typeof(Int64) || _id.DataType == typeof(Int64?) ? BsonAutoId.Int64 : _id.DataType == typeof(Guid) || _id.DataType == typeof(Guid?) ? BsonAutoId.Guid : BsonAutoId.ObjectId; } else { _autoId = BsonAutoId.ObjectId; } } }
/// <summary> /// Register a property as a DbRef - implement a custom Serialize/Deserialize actions to convert entity to $id, $ref only /// </summary> private static void RegisterDbRefItem(BsonMapper mapper, MemberMapper member, string collection) { // get entity var entity = mapper.GetEntityMapper(member.DataType); member.Serialize = (obj, m) => { var idField = entity.Id; var id = idField.Getter(obj); return(new BsonDocument { { "$id", new BsonValue(id) }, { "$ref", collection } }); }; member.Deserialize = (bson, m) => { var idRef = bson.AsDocument["$id"]; return(m.Deserialize(entity.ForType, idRef.IsNull ? bson : // if has no $id object was full loaded (via Include) - so deserialize using normal function new BsonDocument { { "_id", idRef } })); // if has $id, deserialize object using only _id object }; }
public LiteCollection(string name, LazyLoad <LiteEngine> engine, BsonMapper mapper, Logger log) { _name = name ?? mapper.ResolveCollectionName(typeof(T)); _engine = engine; _mapper = mapper; _log = log; _visitor = new QueryVisitor <T>(mapper); _includes = new List <string>(); // if strong typed collection, get _id member mapped (if exists) if (typeof(T) != typeof(BsonDocument)) { var entity = mapper.GetEntityMapper(typeof(T)); _id = entity.Id; if (_id != null && _id.AutoId) { _autoId = _id.DataType == typeof(ObjectId) ? BsonType.ObjectId : _id.DataType == typeof(Guid) ? BsonType.Guid : _id.DataType == typeof(DateTime) ? BsonType.DateTime : _id.DataType == typeof(Int32) ? BsonType.Int32 : _id.DataType == typeof(Int64) ? BsonType.Int64 : BsonType.Null; } } }
/// <summary> /// Register a property as a DbRefList - implement a custom Serialize/Deserialize actions to convert entity to $id, $ref only /// </summary> private static void RegisterDbRefList(BsonMapper mapper, MemberMapper member, string collection) { // get entity from list item type var entity = mapper.GetEntityMapper(member.UnderlyingType); member.Serialize = (list, m) => { var result = new BsonArray(); var idField = entity.Id; foreach (var item in (IEnumerable)list) { if (item == null) { continue; } result.Add(new BsonDocument { { "$id", new BsonValue(idField.Getter(item)) }, { "$ref", collection } }); } return(result); }; member.Deserialize = (bson, m) => { var array = bson.AsArray; if (array.Count == 0) { return(m.Deserialize(member.DataType, array)); } var hasIdRef = array[0].AsDocument == null || array[0].AsDocument["$id"].IsNull; if (hasIdRef) { // if no $id, deserialize as full (was loaded via Include) return(m.Deserialize(member.DataType, array)); } else { // copy array changing $id to _id var arr = new BsonArray(); foreach (var item in array) { arr.Add(new BsonDocument { { "_id", item.AsDocument["$id"] } }); } return(m.Deserialize(member.DataType, arr)); } }; }
/// <summary> /// Register a property as a DbRef - implement a custom Serialize/Deserialize actions to convert entity to $id, $ref only /// </summary> private static void RegisterDbRefItem(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { // get entity var entity = mapper.GetEntityMapper(member.DataType); member.Serialize = (obj, m) => { // supports null values when "SerializeNullValues = true" if (obj == null) { return(BsonValue.Null); } var idField = entity.Id; // #768 if using DbRef with interface with no ID mapped if (idField == null) { throw new LiteException(0, "There is no _id field mapped in your type: " + member.DataType.FullName); } var id = idField.Getter(obj); var bsonDocument = new BsonDocument { ["$id"] = m.Serialize(id.GetType(), id, 0), ["$ref"] = collection }; if (member.DataType != obj.GetType()) { bsonDocument["$type"] = typeNameBinder.GetName(obj.GetType()); } return(bsonDocument); }; member.Deserialize = (bson, m) => { var idRef = bson.IsDocument ? bson["$id"] : BsonValue.Null; var missing = bson.IsDocument ? (bson["$missing"] == true) : false; if (missing) { return(null); } return(m.Deserialize(entity.ForType, idRef.IsNull ? bson : // if has no $id object was full loaded (via Include) - so deserialize using normal function bson.AsDocument.ContainsKey("$type") ? new BsonDocument { ["_id"] = idRef, ["_type"] = bson["$type"] } : new BsonDocument { ["_id"] = idRef })); // if has $id, deserialize object using only _id object }; }
/// <summary> /// Register a property as a DbRef - implement a custom Serialize/Deserialize actions to convert entity to $id, $ref only /// </summary> private static void RegisterDbRefItem(BsonMapper mapper, MemberMapper member, string collection) { // get entity var entity = mapper.GetEntityMapper(member.DataType); member.Serialize = (obj, m) => { // supports null values when "SerializeNullValues = true" if (obj == null) { return(BsonValue.Null); } var idField = entity.Id; // #768 if using DbRef with interface with no ID mapped if (idField == null) { throw new LiteException("There is no _id field mapped in your type: " + member.DataType.FullName); } var id = idField.Getter(obj); var bsonDocument = new BsonDocument { { "$id", m.Serialize(id.GetType(), id, 0) }, { "$ref", collection } }; if (member.DataType.GetTypeInfo().IsInterface || member.DataType.GetTypeInfo().IsAbstract) { bsonDocument["$type"] = obj.GetType().FullName + ", " + obj.GetType().GetTypeInfo().Assembly.GetName().Name; } return(bsonDocument); }; member.Deserialize = (bson, m) => { var idRef = bson.AsDocument["$id"]; return(m.Deserialize(entity.ForType, idRef.IsNull ? bson : // if has no $id object was full loaded (via Include) - so deserialize using normal function member.DataType.GetTypeInfo().IsAbstract || member.DataType.GetTypeInfo().IsInterface ? new BsonDocument { { "_id", idRef }, { "_type", bson.AsDocument["$type"] } } : new BsonDocument { { "_id", idRef } })); // if has $id, deserialize object using only _id object }; }
/// <summary> /// Returns document field name for some type member /// </summary> private string ResolveMember(MemberInfo member) { var name = member.Name; // get class entity from mapper var entity = _mapper.GetEntityMapper(member.DeclaringType); var field = entity.Members.FirstOrDefault(x => x.MemberName == name); if (field == null) { throw new NotSupportedException($"Member {name} not found on BsonMapper for type {member.DeclaringType}."); } return("." + field.FieldName); }
/// <summary> /// Based on a LINQ expression, returns document field mapped from class Property. /// Support multi level dotted notation: x => x.Customer.Name /// Prefix is used on array expression like: x => x.Customers.Any(z => z.Name == "John") /// </summary> public string GetField(Expression expr, string prefix = "", bool showArrayItems = false) { var property = prefix + expr.GetPath(); var parts = property.Split('.'); var fields = new string[parts.Length]; var type = _type; var isdbref = false; // loop "first.second.last" for (var i = 0; i < parts.Length; i++) { var entity = _mapper.GetEntityMapper(type); var part = parts[i]; var prop = entity.Members.Find(x => x.MemberName == part); if (prop == null) { throw new NotSupportedException(property + " not mapped in " + type.Name); } // if property is an IEnumerable, gets underlying type (otherwise, gets PropertyType) type = prop.UnderlyingType; fields[i] = prop.FieldName; if (showArrayItems && prop.IsList) { fields[i] += "[*]"; } if (prop.FieldName == "_id" && isdbref) { isdbref = false; fields[i] = "$id"; } // if this property is DbRef, so if next property is _id, change to $id if (prop.IsDbRef) { isdbref = true; } } return(string.Join(".", fields)); }
/// <summary> /// Based on an expression, returns document field mapped from class Property. /// Support multi level dotted notation: x => x.Customer.Name /// Prefix is used on array expression like: x => x.Customers.Any(z => z.Name == "John") (prefix = "Customers." /// </summary> public string GetField(Expression expr, string prefix = "") { var property = prefix + expr.GetPath(); var parts = property.Split('.'); var fields = new string[parts.Length]; var type = _type; var isdbref = false; // loop "first.second.last" for (var i = 0; i < parts.Length; i++) { var entity = _mapper.GetEntityMapper(type); var part = parts[i]; var prop = entity.Members.Find(x => x.MemberName == part); if (prop == null) { throw LiteException.PropertyNotMapped(property); } // if property is a IEnumerable, gets underlayer type (otherwise, gets PropertyType) type = prop.UnderlyingType; fields[i] = prop.FieldName; if (prop.FieldName == "_id" && isdbref) { isdbref = false; fields[i] = "$id"; } // if this property is DbRef, so if next property is _id, change to $id if (prop.IsDbRef) { isdbref = true; } } return(string.Join(".", fields)); }
/// <summary> /// Returns document field name for some type member /// </summary> private string ResolveMember(MemberInfo member) { var name = member.Name; // checks if parent field are not DbRef (checks for same dataType) var isParentDbRef = _dbRefType != null && _dbRefType == member.DeclaringType; // get class entity from mapper var entity = _mapper.GetEntityMapper(member.DeclaringType); // get mapped field from entity var field = entity.Members.FirstOrDefault(x => x.MemberName == name); if (field == null) { throw new NotSupportedException($"Member {name} not found on BsonMapper for type {member.DeclaringType}."); } // define if this field are DbRef (child will need check parent) _dbRefType = field.IsDbRef ? field.UnderlyingType : null; // if parent call is DbRef and are calling _id field, rename to $id return("." + (isParentDbRef && field.FieldName == "_id" ? "$id" : field.FieldName)); }
/// <summary> /// Register a property as a DbRef - implement a custom Serialize/Deserialize actions to convert entity to $id, $ref only /// </summary> private static void RegisterDbRefItem(BsonMapper mapper, MemberMapper member, string collection) { // get entity var entity = mapper.GetEntityMapper(member.DataType); member.Serialize = (obj, m) => { var idField = entity.Id; // #768 if using DbRef with interface with no ID mapped if (idField == null) { throw new LiteException("There is no _id field mapped in your type: " + member.DataType.FullName); } var id = idField.Getter(obj); return(new BsonDocument { { "$id", new BsonValue(id) }, { "$ref", collection } }); }; member.Deserialize = (bson, m) => { var idRef = bson.AsDocument["$id"]; return(m.Deserialize(entity.ForType, idRef.IsNull ? bson : // if has no $id object was full loaded (via Include) - so deserialize using normal function new BsonDocument { { "_id", idRef } })); // if has $id, deserialize object using only _id object }; }
internal EntityBuilder(BsonMapper mapper, ITypeNameBinder typeNameBinder) { _mapper = mapper; _typeNameBinder = typeNameBinder; _entity = mapper.GetEntityMapper(typeof(T)); }
/// <summary> /// Register a property as a DbRefList - implement a custom Serialize/Deserialize actions to convert entity to $id, $ref only /// </summary> private static void RegisterDbRefList(BsonMapper mapper, MemberMapper member, string collection) { // get entity from list item type var entity = mapper.GetEntityMapper(member.UnderlyingType); member.Serialize = (list, m) => { // supports null values when "SerializeNullValues = true" if (list == null) { return(BsonValue.Null); } var result = new BsonArray(); var idField = entity.Id; foreach (var item in (IEnumerable)list) { if (item == null) { continue; } var id = idField.Getter(item); result.Add(new BsonDocument { { "$id", m.Serialize(id.GetType(), id, 0) }, { "$ref", collection } }); } return(result); }; member.Deserialize = (bson, m) => { var array = bson.AsArray; if (array.Count == 0) { return(m.Deserialize(member.DataType, array)); } // copy array changing $id to _id var result = new BsonArray(); foreach (var item in array) { var refId = item.AsDocument["$id"]; // if refId is null was included by "include" query, so "item" is full filled document if (refId.IsNull) { result.Add(item); } else { result.Add(new BsonDocument { { "_id", refId } }); } } return(m.Deserialize(member.DataType, result)); }; }
internal EntityBuilder(BsonMapper mapper) { _mapper = mapper; _entity = mapper.GetEntityMapper(typeof(T)); }
/// <summary> /// Register a property as a DbRefList - implement a custom Serialize/Deserialize actions to convert entity to $id, $ref only /// </summary> private static void RegisterDbRefList(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { // get entity from list item type var entity = mapper.GetEntityMapper(member.UnderlyingType); member.Serialize = (list, m) => { // supports null values when "SerializeNullValues = true" if (list == null) { return(BsonValue.Null); } var result = new BsonArray(); var idField = entity.Id; foreach (var item in (IEnumerable)list) { if (item == null) { continue; } var id = idField.Getter(item); var bsonDocument = new BsonDocument { ["$id"] = m.Serialize(id.GetType(), id, 0), ["$ref"] = collection }; if (member.UnderlyingType != item.GetType()) { bsonDocument["$type"] = typeNameBinder.GetName(item.GetType()); } result.Add(bsonDocument); } return(result); }; member.Deserialize = (bson, m) => { if (bson.IsArray == false) { return(null); } var array = bson.AsArray; if (array.Count == 0) { return(m.Deserialize(member.DataType, array)); } // copy array changing $id to _id var result = new BsonArray(); foreach (var item in array) { if (item.IsDocument == false) { continue; } var doc = item.AsDocument; var idRef = doc["$id"]; var missing = doc["$missing"] == true; var included = doc.ContainsKey("$ref") == false; // if referece document are missing, do not inlcude on output list if (missing) { continue; } // if refId is null was included by "include" query, so "item" is full filled document if (included) { item["_id"] = idRef; if (item.AsDocument.ContainsKey("$type")) { item["_type"] = item["$type"]; } result.Add(item); } else { var bsonDocument = new BsonDocument { ["_id"] = idRef }; if (item.AsDocument.ContainsKey("$type")) { bsonDocument["_type"] = item["$type"]; } result.Add(bsonDocument); } } return(m.Deserialize(member.DataType, result)); }; }
/// <summary> /// Register a property as a DbRef - implement a custom Serialize/Deserialize actions to convert entity to $id, $ref only /// </summary> private static void RegisterDbRefItem(BsonMapper mapper, MemberMapper member, ITypeNameBinder typeNameBinder, string collection) { // get entity var entity = mapper.GetEntityMapper(member.DataType); member.Serialize = (obj, m) => { // supports null values when "SerializeNullValues = true" if (obj == null) { return(BsonValue.Null); } var idField = entity.Id; // #768 if using DbRef with interface with no ID mapped if (idField == null) { throw new LiteException(0, "There is no _id field mapped in your type: " + member.DataType.FullName); } var id = idField.Getter(obj); var bsonDocument = new BsonDocument { ["$id"] = m.Serialize(id.GetType(), id, 0), ["$ref"] = collection }; if (member.DataType != obj.GetType()) { bsonDocument["$type"] = typeNameBinder.GetName(obj.GetType()); } return(bsonDocument); }; member.Deserialize = (bson, m) => { // if not a document (maybe BsonValue.null) returns null if (bson == null || bson.IsDocument == false) { return(null); } var doc = bson.AsDocument; var idRef = doc["$id"]; var missing = doc["$missing"] == true; var included = doc.ContainsKey("$ref") == false; if (missing) { return(null); } if (included) { doc["_id"] = idRef; if (doc.ContainsKey("$type")) { doc["_type"] = bson["$type"]; } return(m.Deserialize(entity.ForType, doc)); } else { return(m.Deserialize(entity.ForType, doc.ContainsKey("$type") ? new BsonDocument { ["_id"] = idRef, ["_type"] = bson["$type"] } : new BsonDocument { ["_id"] = idRef })); // if has $id, deserialize object using only _id object } }; }