Mercurial > pub > bltoolkit
diff Source/Reflection/MetadataProvider/AttributeMetadataProvider.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Reflection/MetadataProvider/AttributeMetadataProvider.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,799 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using BLToolkit.Data.Sql.SqlProvider; + +using BLToolkit.TypeBuilder; + +namespace BLToolkit.Reflection.MetadataProvider +{ + using DataAccess; + using Extension; + using Mapping; + + public class AttributeMetadataProvider : MetadataProviderBase + { + #region Helpers + + private TypeAccessor _typeAccessor; + private object[] _mapFieldAttributes; + private object[] _nonUpdatableAttributes; + readonly object _sync = new object(); + + void EnsureMapper(TypeAccessor typeAccessor) + { + if (_typeAccessor != typeAccessor) + { + _typeAccessor = typeAccessor; + _mapFieldAttributes = null; + _nonUpdatableAttributes = null; + } + } + + protected object[] GetMapFieldAttributes(TypeAccessor typeAccessor) + { + lock (_sync) + { + EnsureMapper(typeAccessor); + + return _mapFieldAttributes ?? (_mapFieldAttributes = TypeHelper.GetAttributes(typeAccessor.Type, typeof (MapFieldAttribute))); + } + } + + object[] GetNonUpdatableAttributes(TypeAccessor typeAccessor) + { + lock (_sync) + { + EnsureMapper(typeAccessor); + + return _nonUpdatableAttributes ?? (_nonUpdatableAttributes = TypeHelper.GetAttributes(typeAccessor.Type, typeof(NonUpdatableAttribute))); + } + } + + #endregion + + #region GetFieldName + + public override string GetFieldName(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + var a = member.GetAttribute<MapFieldAttribute>(); + + if (a != null && a.MapName != null) + { + isSet = true; + return a.MapName; + } + + foreach (MapFieldAttribute attr in GetMapFieldAttributes(member.TypeAccessor)) + { + if (attr.MapName != null && string.Equals(attr.OrigName, member.Name, StringComparison.InvariantCultureIgnoreCase)) + { + isSet = true; + return attr.MapName; + } + } + + return base.GetFieldName(typeExtension, member, out isSet); + } + + #endregion + + #region GetFieldStorage + + public override string GetFieldStorage(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + var a = member.GetAttribute<MapFieldAttribute>(); + + if (a != null) + { + isSet = true; + return a.Storage; + } + + foreach (MapFieldAttribute attr in GetMapFieldAttributes(member.TypeAccessor)) + { + if (string.Equals(attr.OrigName, member.Name, StringComparison.InvariantCultureIgnoreCase)) + { + isSet = true; + return attr.Storage; + } + } + + return base.GetFieldStorage(typeExtension, member, out isSet); + } + + #endregion + + #region GetInheritanceDiscriminator + + public override bool GetInheritanceDiscriminator(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + var a = member.GetAttribute<MapFieldAttribute>(); + + if (a != null) + { + isSet = true; + return a.IsInheritanceDiscriminator; + } + + foreach (MapFieldAttribute attr in GetMapFieldAttributes(member.TypeAccessor)) + { + if (string.Equals(attr.OrigName, member.Name, StringComparison.InvariantCultureIgnoreCase)) + { + isSet = true; + return attr.IsInheritanceDiscriminator; + } + } + + return base.GetInheritanceDiscriminator(typeExtension, member, out isSet); + } + + #endregion + + #region EnsureMapper + + public override void EnsureMapper(TypeAccessor typeAccessor, MappingSchema mappingSchema, EnsureMapperHandler handler) + { + foreach (MapFieldAttribute attr in GetMapFieldAttributes(typeAccessor)) + { + if (attr.OrigName != null) + handler(attr.MapName, attr.OrigName); + else + { + var ma = typeAccessor[attr.MapName]; + + foreach (MemberMapper inner in mappingSchema.GetObjectMapper(ma.Type)) + handler(string.Format(attr.Format, inner.Name), ma.Name + "." + inner.MemberName); + } + } + } + + #endregion + + #region GetMapIgnore + + public override bool GetMapIgnore(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + var attr = member.GetAttribute<MapIgnoreAttribute>() ?? (MapIgnoreAttribute)TypeHelper.GetFirstAttribute(member.Type, typeof(MapIgnoreAttribute)); + + if (attr != null) + { + isSet = true; + return attr.Ignore; + } + + if (member.GetAttribute<MapFieldAttribute>() != null || + member.GetAttribute<MapImplicitAttribute>() != null || + TypeHelper.GetFirstAttribute(member.Type, typeof(MapImplicitAttribute)) != null) + { + isSet = true; + return false; + } + + return base.GetMapIgnore(typeExtension, member, out isSet) || member.GetAttribute<AssociationAttribute>() != null; + } + + #endregion + + #region GetMapField + + public override MapFieldAttribute GetMapField(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + var attr = member.GetAttribute<MapFieldAttribute>() ?? (MapFieldAttribute)TypeHelper.GetFirstAttribute(member.Type, typeof(MapFieldAttribute)); + + if (attr != null) + { + isSet = true; + return attr; + } + + return base.GetMapField(typeExtension, member, out isSet); + } + + #endregion + + #region GetDbType + + [CLSCompliant(false)] + public override DbTypeAttribute GetDbType(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + var attr = member.GetAttribute<DbTypeAttribute>() ?? (DbTypeAttribute)TypeHelper.GetFirstAttribute(member.Type, typeof(DbTypeAttribute)); + + if (attr != null) + { + isSet = true; + return attr; + } + + return base.GetDbType(typeExtension, member, out isSet); + } + + #endregion + + #region GetPrimaryKey + + public override PrimaryKeyAttribute GetPrimaryKey(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + var attr = member.GetAttribute<PrimaryKeyAttribute>() ?? (PrimaryKeyAttribute)TypeHelper.GetFirstAttribute(member.Type, typeof(PrimaryKeyAttribute)); + + if (attr != null) + { + isSet = true; + return attr; + } + + return base.GetPrimaryKey(typeExtension, member, out isSet); + } + + #endregion + + #region GetTrimmable + + public override bool GetTrimmable(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + if (member.Type == typeof(string)) + { + var attr = member.GetAttribute<TrimmableAttribute>(); + + if (attr != null) + { + isSet = true; + return attr.IsTrimmable; + } + + attr = (TrimmableAttribute)TypeHelper.GetFirstAttribute( + member.MemberInfo.DeclaringType, typeof(TrimmableAttribute)); + + if (attr != null) + { + isSet = true; + return attr.IsTrimmable; + } + } + + return base.GetTrimmable(typeExtension, member, out isSet); + } + + #endregion + + #region GetMapValues + + public override MapValue[] GetMapValues(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + List<MapValue> list = null; + + object[] attrs = member.GetAttributes<MapValueAttribute>(); + + if (attrs != null) + { + list = new List<MapValue>(attrs.Length); + + foreach (MapValueAttribute a in attrs) + list.Add(new MapValue(a.OrigValue, a.Values)); + } + + attrs = member.GetTypeAttributes(typeof(MapValueAttribute)); + + var memberType = TypeHelper.UnwrapNullableType(member.Type); + + if (attrs != null && attrs.Length > 0) + { + if (list == null) + list = new List<MapValue>(attrs.Length); + + foreach (MapValueAttribute a in attrs) + if (a.Type == null && a.OrigValue != null && a.OrigValue.GetType() == memberType || + a.Type is Type && (Type)a.Type == memberType) + list.Add(new MapValue(a.OrigValue, a.Values)); + } + + var typeMapValues = GetMapValues(typeExtension, memberType, out isSet); + + if (list == null) + return typeMapValues; + + if (typeMapValues != null) + list.AddRange(typeMapValues); + + isSet = true; + + return list.ToArray(); + } + + const FieldAttributes EnumField = FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.Literal; + + static List<MapValue> GetEnumMapValues(Type type) + { + var list = null as List<MapValue>; + var fields = type.GetFields(); + + foreach (var fi in fields) + { + if ((fi.Attributes & EnumField) == EnumField) + { + var enumAttributes = Attribute.GetCustomAttributes(fi, typeof(MapValueAttribute)); + + foreach (MapValueAttribute attr in enumAttributes) + { + if (list == null) + list = new List<MapValue>(fields.Length); + + var origValue = Enum.Parse(type, fi.Name, false); + + list.Add(new MapValue(origValue, attr.Values)); + } + } + } + + return list; + } + + public override MapValue[] GetMapValues(TypeExtension typeExtension, Type type, out bool isSet) + { + List<MapValue> list = null; + + if (TypeHelper.IsNullable(type)) + type = type.GetGenericArguments()[0]; + + if (type.IsEnum) + list = GetEnumMapValues(type); + + var attrs = TypeHelper.GetAttributes(type, typeof(MapValueAttribute)); + + if (attrs != null && attrs.Length != 0) + { + if (list == null) + list = new List<MapValue>(attrs.Length); + + for (var i = 0; i < attrs.Length; i++) + { + var a = (MapValueAttribute)attrs[i]; + list.Add(new MapValue(a.OrigValue, a.Values)); + } + } + + isSet = list != null; + + return isSet? list.ToArray(): null; + } + + #endregion + + #region GetDefaultValue + + public override object GetDefaultValue(MappingSchema mappingSchema, TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + // Check member [DefaultValue(0)] + // + var attr = member.GetAttribute<DefaultValueAttribute>(); + + if (attr != null) + { + isSet = true; + return attr.Value; + } + + // Check type [DefaultValues(typeof(int), 0)] + // + var attrs = member.GetTypeAttributes(typeof(DefaultValueAttribute)); + + foreach (DefaultValueAttribute a in attrs) + if (a.Type == null && a.Value != null && a.Value.GetType() == member.Type || + a.Type != null && a.Type == member.Type) + { + isSet = true; + return a.Value; + } + + return GetDefaultValue(mappingSchema, typeExtension, member.Type, out isSet); + } + + public override object GetDefaultValue(MappingSchema mappingSchema, TypeExtension typeExtension, Type type, out bool isSet) + { + object value = null; + + if (type.IsEnum) + value = GetEnumDefaultValueFromType(type); + + if (value == null) + { + var attrs = TypeHelper.GetAttributes(type, typeof(DefaultValueAttribute)); + + if (attrs != null && attrs.Length != 0) + value = ((DefaultValueAttribute)attrs[0]).Value; + } + + isSet = value != null; + + return TypeExtension.ChangeType(value, type); + } + + private static object GetEnumDefaultValueFromType(Type type) + { + var fields = type.GetFields(); + + foreach (var fi in fields) + { + if ((fi.Attributes & EnumField) == EnumField) + { + var attrs = Attribute.GetCustomAttributes(fi, typeof(DefaultValueAttribute)); + + if (attrs.Length > 0) + return Enum.Parse(type, fi.Name, false); + } + } + + return null; + } + + #endregion + + #region GetNullable + + public override bool GetNullable(MappingSchema mappingSchema, TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + // Check member [Nullable(true | false)] + // + var attr1 = member.GetAttribute<NullableAttribute>(); + + if (attr1 != null) + { + isSet = true; + return attr1.IsNullable; + } + + // Check member [NullValue(0)] + // + var attr2 = member.GetAttribute<NullValueAttribute>(); + + if (attr2 != null) + return isSet = true; + + // Check type [Nullable(true || false)] + // + attr1 = (NullableAttribute)TypeHelper.GetFirstAttribute( + member.MemberInfo.DeclaringType, typeof(NullableAttribute)); + + if (attr1 != null) + { + isSet = true; + return attr1.IsNullable; + } + + // Check type [NullValues(typeof(int), 0)] + // + var attrs = member.GetTypeAttributes(typeof(NullValueAttribute)); + + foreach (NullValueAttribute a in attrs) + if (a.Type == null && a.Value != null && a.Value.GetType() == member.Type || + a.Type != null && a.Type == member.Type) + return isSet = true; + + if (member.Type.IsEnum) + return isSet = mappingSchema.GetNullValue(member.Type) != null; + + if (member.Type.IsClass) + { + var pk = member.GetAttribute<PrimaryKeyAttribute>(); + + if (pk != null) + { + isSet = false; + return false; + } + } + + return base.GetNullable(mappingSchema, typeExtension, member, out isSet); + } + + #endregion + + #region GetLazyInstance + + public override bool GetLazyInstance(MappingSchema mappingSchema, TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + var attr1 = member.GetAttribute<LazyInstanceAttribute>(); + + if (attr1 != null) + { + isSet = true; + return attr1.IsLazy; + } + + attr1 = (LazyInstanceAttribute)TypeHelper.GetFirstAttribute(member.MemberInfo.DeclaringType, typeof(LazyInstanceAttribute)); + + if (attr1 != null) + { + isSet = true; + return attr1.IsLazy; + } + + return base.GetLazyInstance(mappingSchema, typeExtension, member, out isSet); + } + + #endregion + + #region GetNullValue + + private static object CheckNullValue(object value, MemberAccessor member) + { + if (value is Type && (Type)value == typeof(DBNull)) + { + value = DBNull.Value; + + if (member.Type == typeof(string)) + value = null; + } + + return value; + } + + public override object GetNullValue(MappingSchema mappingSchema, TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + // Check member [NullValue(0)] + // + var attr = member.GetAttribute<NullValueAttribute>(); + + if (attr != null) + { + isSet = true; + return CheckNullValue(attr.Value, member); + } + + // Check type [NullValues(typeof(int), 0)] + // + var attrs = member.GetTypeAttributes(typeof(NullValueAttribute)); + + foreach (NullValueAttribute a in attrs) + { + if (a.Type == null && a.Value != null && a.Value.GetType() == member.Type || + a.Type != null && a.Type == member.Type) + { + isSet = true; + return CheckNullValue(a.Value, member); + } + } + + if (member.Type.IsEnum) + { + var value = CheckNullValue(mappingSchema.GetNullValue(member.Type), member); + + if (value != null) + { + isSet = true; + return value; + } + } + + isSet = false; + return null; + } + + #endregion + + #region GetDbName + + public override string GetDatabaseName(Type type, ExtensionList extensions, out bool isSet) + { + var attrs = type.GetCustomAttributes(typeof(TableNameAttribute), true); + + if (attrs.Length > 0) + { + var name = ((TableNameAttribute)attrs[0]).Database; + isSet = name != null; + return name; + } + + return base.GetDatabaseName(type, extensions, out isSet); + } + + #endregion + + #region GetTableName + + public override string GetOwnerName(Type type, ExtensionList extensions, out bool isSet) + { + var attrs = type.GetCustomAttributes(typeof(TableNameAttribute), true); + + if (attrs.Length > 0) + { + var name = ((TableNameAttribute)attrs[0]).Owner; + isSet = name != null; + return name; + } + + return base.GetOwnerName(type, extensions, out isSet); + } + + #endregion + + #region GetTableName + + public override string GetTableName(Type type, ExtensionList extensions, out bool isSet) + { + var attrs = type.GetCustomAttributes(typeof(TableNameAttribute), true); + + if (attrs.Length > 0) + { + var name = ((TableNameAttribute)attrs[0]).Name; + isSet = name != null; + return name; + } + + return base.GetTableName(type, extensions, out isSet); + } + + #endregion + + #region GetPrimaryKeyOrder + + public override int GetPrimaryKeyOrder(Type type, TypeExtension typeExt, MemberAccessor member, out bool isSet) + { + var attr = member.GetAttribute<PrimaryKeyAttribute>(); + + if (attr != null) + { + isSet = true; + return attr.Order; + } + + return base.GetPrimaryKeyOrder(type, typeExt, member, out isSet); + } + + #endregion + + public override string GetSequenceName(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + var attr = member.GetAttribute<SequenceNameAttribute>(); + + if (attr != null) + { + isSet = true; + return attr.SequenceName; + } + + return base.GetSequenceName(typeExtension, member, out isSet); + } + + #region GetNonUpdatableFlag + + public override NonUpdatableAttribute GetNonUpdatableAttribute(Type type, TypeExtension typeExt, MemberAccessor member, out bool isSet) + { + var attr = member.GetAttribute<NonUpdatableAttribute>(); + + if (attr != null) + { + isSet = true; + return attr; + } + + foreach (NonUpdatableAttribute a in GetNonUpdatableAttributes(member.TypeAccessor)) + { + if (string.Equals(a.FieldName, member.Name, StringComparison.InvariantCultureIgnoreCase)) + { + isSet = true; + return a; + } + } + + return base.GetNonUpdatableAttribute(type, typeExt, member, out isSet); + } + + #endregion + + #region GetSqlIgnore + + public override bool GetSqlIgnore(TypeExtension typeExtension, MemberAccessor member, out bool isSet) + { + var attr = member.GetAttribute<SqlIgnoreAttribute>(); + + if (attr == null) + attr = (SqlIgnoreAttribute)TypeHelper.GetFirstAttribute(member.Type, typeof(SqlIgnoreAttribute)); + + if (attr != null) + { + isSet = true; + return attr.Ignore; + } + + return base.GetSqlIgnore(typeExtension, member, out isSet); + } + + #endregion + + #region GetRelations + + public override List<MapRelationBase> GetRelations(MappingSchema schema, ExtensionList typeExt, Type master, Type slave, out bool isSet) + { + var masterAccessor = TypeAccessor.GetAccessor(master); + var slaveAccessor = slave != null ? TypeAccessor.GetAccessor(slave) : null; + var relations = new List<MapRelationBase>(); + + foreach (MemberAccessor ma in masterAccessor) + { + var attr = ma.GetAttribute<RelationAttribute>(); + + if (attr == null || (slave != null && attr.Destination != slave && ma.Type != slave)) + continue; + + if (slave == null) + slaveAccessor = TypeAccessor.GetAccessor(attr.Destination ?? ma.Type); + + + var toMany = TypeHelper.IsSameOrParent(typeof(IEnumerable), ma.Type); + + if (toMany && attr.Destination == null) + throw new InvalidOperationException("Destination type should be set for enumerable relations: " + ma.Type.FullName + "." + ma.Name); + + var masterIndex = attr.MasterIndex; + var slaveIndex = attr.SlaveIndex; + + if (slaveIndex == null) + { + var accessor = toMany ? masterAccessor : slaveAccessor; + var tex = TypeExtension.GetTypeExtension(accessor.Type, typeExt); + var keys = GetPrimaryKeyFields(schema, accessor, tex); + + if (keys.Count > 0) + slaveIndex = new MapIndex(keys.ToArray()); + } + + if (slaveIndex == null) + throw new InvalidOperationException("Slave index is not set for relation: " + ma.Type.FullName + "." + ma.Name); + + if (masterIndex == null) + masterIndex = slaveIndex; + + var relation = new MapRelationBase(attr.Destination ?? ma.Type, slaveIndex, masterIndex, ma.Name); + + relations.Add(relation); + } + + isSet = true; + return relations; + } + + #endregion + + #region GetAssociation + + public override Association GetAssociation(TypeExtension typeExtension, MemberAccessor member) + { + var aa = member.GetAttribute<AssociationAttribute>(); + + if (aa == null) + return base.GetAssociation(typeExtension, member); + + return new Association( + member, + aa.GetThisKeys(), + aa.GetOtherKeys(), + aa.Storage, + aa.CanBeNull); + } + + #endregion + + #region GetInheritanceMapping + + public override InheritanceMappingAttribute[] GetInheritanceMapping(Type type, TypeExtension typeExtension) + { + var attrs = type.GetCustomAttributes(typeof(InheritanceMappingAttribute), true); + + if (attrs.Length > 0) + { + var maps = new InheritanceMappingAttribute[attrs.Length]; + + for (var i = 0; i < attrs.Length; i++) + maps[i] = (InheritanceMappingAttribute)attrs[i]; + + return maps; + } + + return base.GetInheritanceMapping(type, typeExtension); + } + + #endregion + } +}