internal EntityBuilder(BsonMapper mapper, ITypeNameBinder typeNameBinder) { _mapper = mapper; _typeNameBinder = typeNameBinder; _entity = mapper.GetEntityMapper(typeof(T)); }
/// <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); }
/// <summary> /// Get best construtor to use to initialize this entity. /// - Look if contains [BsonCtor] attribute /// - Look for parameterless ctor /// - Look for first contructor with parameter and use BsonDocument to send RawValue /// </summary> protected virtual CreateObject GetTypeCtor(EntityMapper mapper) { var ctors = mapper.ForType.GetConstructors(); var ctor = ctors.FirstOrDefault(x => x.GetCustomAttribute <BsonCtorAttribute>() != null && x.GetParameters().All(p => Reflection.ConvertType.ContainsKey(p.ParameterType) || _basicTypes.Contains(p.ParameterType) || p.ParameterType.GetTypeInfo().IsEnum)) ?? ctors.FirstOrDefault(x => x.GetParameters().Length == 0) ?? ctors.FirstOrDefault(x => x.GetParameters().All(p => Reflection.ConvertType.ContainsKey(p.ParameterType) || _customDeserializer.ContainsKey(p.ParameterType) || _basicTypes.Contains(p.ParameterType) || p.ParameterType.GetTypeInfo().IsEnum)); if (ctor == null) { return(null); } var pars = new List <Expression>(); var pDoc = Expression.Parameter(typeof(BsonDocument), "_doc"); // otherwise, need access ctor with parameter foreach (var p in ctor.GetParameters()) { // try first get converted named (useful for Id => _id) var name = mapper.Members.FirstOrDefault(x => x.MemberName.Equals(p.Name, StringComparison.OrdinalIgnoreCase))?.FieldName ?? p.Name; var expr = Expression.MakeIndex(pDoc, Reflection.DocumentItemProperty, new[] { Expression.Constant(name) }); if (_customDeserializer.TryGetValue(p.ParameterType, out var func)) { var deserializer = Expression.Constant(func); var call = Expression.Invoke(deserializer, expr); var cast = Expression.Convert(call, p.ParameterType); pars.Add(cast); } else if (_basicTypes.Contains(p.ParameterType)) { var typeExpr = Expression.Constant(p.ParameterType); var rawValue = Expression.Property(expr, typeof(BsonValue).GetProperty("RawValue")); var convertTypeFunc = Expression.Call(typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type) }), rawValue, typeExpr); var cast = Expression.Convert(convertTypeFunc, p.ParameterType); pars.Add(cast); } else if (p.ParameterType.GetTypeInfo().IsEnum&& this.EnumAsInteger) { var typeExpr = Expression.Constant(p.ParameterType); var rawValue = Expression.PropertyOrField(expr, "AsInt32"); var convertTypeFunc = Expression.Call(typeof(Enum).GetMethod("ToObject", new Type[] { typeof(Type), typeof(Int32) }), typeExpr, rawValue); var cast = Expression.Convert(convertTypeFunc, p.ParameterType); pars.Add(cast); } else if (p.ParameterType.GetTypeInfo().IsEnum) { var typeExpr = Expression.Constant(p.ParameterType); var rawValue = Expression.PropertyOrField(expr, "AsString"); var convertTypeFunc = Expression.Call(typeof(Enum).GetMethod("Parse", new Type[] { typeof(Type), typeof(string) }), typeExpr, rawValue); var cast = Expression.Convert(convertTypeFunc, p.ParameterType); pars.Add(cast); } else { var propInfo = Reflection.ConvertType[p.ParameterType]; var prop = Expression.Property(expr, propInfo); pars.Add(prop); } } // get `new MyClass([params])` expression var newExpr = Expression.New(ctor, pars.ToArray()); // get lambda expression var fn = mapper.ForType.GetTypeInfo().IsClass ? Expression.Lambda <CreateObject>(newExpr, pDoc).Compile() : // Class Expression.Lambda <CreateObject>(Expression.Convert(newExpr, typeof(object)), pDoc).Compile(); // Struct return(fn); }
internal EntityBuilder(BsonMapper mapper) { _mapper = mapper; _entity = mapper.GetEntityMapper(typeof(T)); }
/// <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(type); var idAttr = typeof(BsonIdAttribute); var ignoreAttr = typeof(BsonIgnoreAttribute); var fieldAttr = typeof(BsonFieldAttribute); var dbrefAttr = typeof(BsonRefAttribute); var members = this.GetTypeMembers(type); var id = this.GetIdMember(members); foreach (var memberInfo in members) { // checks [BsonIgnore] if (CustomAttributeExtensions.IsDefined(memberInfo, ignoreAttr, true)) { continue; } // checks field name conversion var name = this.ResolveFieldName(memberInfo.Name); // check if property has [BsonField] var field = (BsonFieldAttribute)CustomAttributeExtensions.GetCustomAttributes(memberInfo, fieldAttr, true).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"; } // 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)CustomAttributeExtensions.GetCustomAttributes(memberInfo, idAttr, true).FirstOrDefault(); // get data type var dataType = memberInfo is PropertyInfo ? (memberInfo as PropertyInfo).PropertyType : (memberInfo as FieldInfo).FieldType; // check if datatype is list/array var isEnumerable = Reflection.IsEnumerable(dataType); // create a property mapper var member = new MemberMapper { AutoId = autoId == null ? true : autoId.AutoId, FieldName = name, MemberName = memberInfo.Name, DataType = dataType, IsEnumerable = isEnumerable, UnderlyingType = isEnumerable ? Reflection.GetListItemType(dataType) : dataType, Getter = getter, Setter = setter }; // check if property has [BsonRef] var dbRef = (BsonRefAttribute)CustomAttributeExtensions.GetCustomAttributes(memberInfo, dbrefAttr, false).FirstOrDefault(); if (dbRef != null && memberInfo is PropertyInfo) { BsonMapper.RegisterDbRef(this, member, _typeNameBinder, dbRef.Collection ?? this.ResolveCollectionName((memberInfo as PropertyInfo).PropertyType)); } // support callback to user modify member mapper this.ResolveMember?.Invoke(type, memberInfo, member); // test if has name and there is no duplicate field if (member.FieldName != null && mapper.Members.Any(x => x.FieldName.Equals(name, StringComparison.OrdinalIgnoreCase)) == false) { mapper.Members.Add(member); } } return(mapper); }