public BsonMapper(Func <Type, object> customTypeInstantiator = null) { this.SerializeNullValues = false; this.TrimWhitespace = true; this.EmptyStringToNull = true; this.ResolveFieldName = (s) => s; this.ResolveMember = (t, mi, mm) => { }; this.ResolveCollectionName = (t) => Reflection.IsList(t) ? Reflection.GetListItemType(t).Name : t.Name; this.IncludeFields = false; _typeInstantiator = customTypeInstantiator ?? Reflection.CreateInstance; #region Register CustomTypes RegisterType <Uri>(uri => uri.AbsoluteUri, bson => new Uri(bson.AsString)); RegisterType <DateTimeOffset>(value => new BsonValue(value.UtcDateTime), bson => bson.AsDateTime.ToUniversalTime()); RegisterType <TimeSpan>(value => new BsonValue(value.Ticks), bson => new TimeSpan(bson.AsInt64)); RegisterType <Regex>( r => r.Options == RegexOptions.None ? new BsonValue(r.ToString()) : new BsonDocument { { "p", r.ToString() }, { "o", (int)r.Options } }, value => value.IsString ? new Regex(value) : new Regex(value.AsDocument["p"].AsString, (RegexOptions)value.AsDocument["o"].AsInt32) ); #endregion Register CustomTypes }
/// <summary> /// Define a subdocument (or a list of) as a reference /// </summary> public EntityBuilder <T> DbRef <K>(Expression <Func <T, K> > property, string collectionName) { if (string.IsNullOrEmpty(collectionName)) { throw new ArgumentNullException("collectionName"); } return(this.GetProperty(property, (p) => { var typeRef = typeof(K); p.IsDbRef = true; if (Reflection.IsList(typeRef)) { var itemType = typeRef.IsArray ? typeRef.GetElementType() : Reflection.UnderlyingTypeOf(typeRef); var mapper = _mapper.GetPropertyMapper(itemType); RegisterDbRefList(p, collectionName, typeRef, itemType, mapper); } else { var mapper = _mapper.GetPropertyMapper(typeRef); RegisterDbRef(p, collectionName, typeRef, mapper); } })); }
/// <summary> /// Visit :: x => x.Customer.Name.`ToUpper()` /// </summary> protected override Expression VisitMethodCall(MethodCallExpression node) { // get method declaring type - if is from any kind of list, read as Enumerable var isList = Reflection.IsList(node.Method.DeclaringType); var declaringType = isList ? typeof(Enumerable) : node.Method.DeclaringType; // if special method for index access, eval index value (do not use parameters) if (this.IsMethodIndexEval(node, out var obj, out var idx)) { this.Visit(obj); var index = this.Evaluate(idx, typeof(string), typeof(int)); if (index is string) { _builder.Append("."); _builder.Append($"['{index}']"); } else { _builder.Append($"[{index}]"); } return(node); } // if not found in resolver, try run method if (!_resolver.TryGetValue(declaringType, out var type)) { // if method are called by parameter expression and it's not exists, throw error var isParam = ParameterExpressionVisitor.Test(node); if (isParam) { throw new NotSupportedException($"Method {node.Method.Name} not available to convert to BsonExpression ({node.ToString()})."); } // otherwise, try compile and execute var value = this.Evaluate(node); base.Visit(Expression.Constant(value)); return(node); } // otherwise I have resolver for this method var pattern = type.ResolveMethod(node.Method); if (pattern == null) { throw new NotSupportedException($"Method {Reflection.MethodName(node.Method)} in {node.Method.DeclaringType.Name} are not supported when convert to BsonExpression ({node.ToString()})."); } // run pattern using object as # and args as @n this.ResolvePattern(pattern, node.Object, node.Arguments); return(node); }
public BsonMapper(Func <Type, object> customTypeInstantiator = null) { this.SerializeNullValues = false; this.TrimWhitespace = true; this.EmptyStringToNull = true; this.ResolveFieldName = (s) => s; this.ResolveMember = (t, mi, mm) => { }; this.ResolveCollectionName = (t) => Reflection.IsList(t) ? Reflection.GetListItemType(t).Name : t.Name; #if NET35 this.IncludeFields = false; #endif _typeInstantiator = customTypeInstantiator ?? Reflection.CreateInstance; #region Register CustomTypes RegisterType <Uri>(uri => uri.AbsoluteUri, bson => new Uri(bson.AsString)); RegisterType <DateTimeOffset>(value => new BsonValue(value.UtcDateTime), bson => bson.AsDateTime.ToUniversalTime()); RegisterType <TimeSpan>(value => new BsonValue(value.Ticks), bson => new TimeSpan(bson.AsInt64)); RegisterType <Regex>( r => r.Options == RegexOptions.None ? new BsonValue(r.ToString()) : new BsonDocument { { "p", r.ToString() }, { "o", (int)r.Options } }, value => value.IsString ? new Regex(value) : new Regex(value.AsDocument["p"].AsString, (RegexOptions)value.AsDocument["o"].AsInt32) ); #endregion Register CustomTypes #region Register AutoId // register AutoId for ObjectId, Guid and Int32 RegisterAutoId ( value => value.Equals(ObjectId.Empty), (db, col) => ObjectId.NewObjectId() ); RegisterAutoId ( value => value == Guid.Empty, (db, col) => Guid.NewGuid() ); RegisterAutoId ( value => value == 0, (db, col) => { var max = db.Max(col, "_id"); return(max.IsMaxValue ? 1 : (max + 1)); } ); #endregion }
/// <summary> /// Try find a Type Resolver for declaring type /// </summary> private bool TryGetResolver(Type declaringType, out ITypeResolver typeResolver) { // get method declaring type - if is from any kind of list, read as Enumerable var isList = Reflection.IsList(declaringType); var isNullable = Reflection.IsNullable(declaringType); var type = isList ? typeof(Enumerable) : isNullable ? typeof(Nullable) : declaringType; return(_resolver.TryGetValue(type, out typeResolver)); }
/// <summary> /// Use this method to override how your class can be, by default, mapped from entity to Bson document. /// Returns an EntityMapper from each requested Type /// </summary> protected virtual EntityMapper BuildEntityMapper(Type type) { var mapper = new EntityMapper { Members = new List <MemberMapper>(), ForType = type }; var idAttr = typeof(BsonIdAttribute); var ignoreAttr = typeof(BsonIgnoreAttribute); var fieldAttr = typeof(BsonFieldAttribute); var indexAttr = typeof(BsonIndexAttribute); var dbrefAttr = typeof(BsonRefAttribute); var members = this.GetTypeMembers(type); var id = this.GetIdMember(members); foreach (var memberInfo in members) { // checks [BsonIgnore] if (memberInfo.IsDefined(ignoreAttr, true)) { continue; } // checks field name conversion var name = this.ResolveFieldName(memberInfo.Name); // check if property has [BsonField] var field = (BsonFieldAttribute)memberInfo.GetCustomAttributes(fieldAttr, false).FirstOrDefault(); // check if property has [BsonField] with a custom field name if (field != null && field.Name != null) { name = field.Name; } // checks if memberInfo is id field if (memberInfo == id) { name = "_id"; } // test if field name is OK (avoid to check in all instances) - do not test internal classes, like DbRef if (BsonDocument.IsValidFieldName(name) == false) { throw LiteException.InvalidFormat(memberInfo.Name); } // create getter/setter function var getter = Reflection.CreateGenericGetter(type, memberInfo); var setter = Reflection.CreateGenericSetter(type, memberInfo); // check if property has [BsonId] to get with was setted AutoId = true var autoId = (BsonIdAttribute)memberInfo.GetCustomAttributes(idAttr, false).FirstOrDefault(); // get data type var dataType = memberInfo is PropertyInfo ? (memberInfo as PropertyInfo).PropertyType : (memberInfo as FieldInfo).FieldType; // check if datatype is list/array var isList = Reflection.IsList(dataType); // create a property mapper var member = new MemberMapper { AutoId = autoId == null ? true : autoId.AutoId, FieldName = name, MemberName = memberInfo.Name, DataType = dataType, IsList = isList, UnderlyingType = isList ? Reflection.GetListItemType(dataType) : dataType, Getter = getter, Setter = setter }; // check if property has [BsonRef] var dbRef = (BsonRefAttribute)memberInfo.GetCustomAttributes(dbrefAttr, false).FirstOrDefault(); if (dbRef != null && memberInfo is PropertyInfo) { BsonMapper.RegisterDbRef(this, member, dbRef.Collection ?? this.ResolveCollectionName((memberInfo as PropertyInfo).PropertyType)); } // support callback to user modify member mapper if (this.ResolveMember != null) { this.ResolveMember(type, memberInfo, member); } // test if has name and there is no duplicate field if (member.FieldName != null && mapper.Members.Any(x => x.FieldName == name) == false) { mapper.Members.Add(member); } } return(mapper); }