Mercurial > pub > bltoolkit
view Source/Mapping/ObjectMapper.cs @ 9:1e85f66cf767 default tip
update bltoolkit
author | nickolay |
---|---|
date | Thu, 05 Apr 2018 20:53:26 +0300 |
parents | f990fcb411a9 |
children |
line wrap: on
line source
using System; using System.Collections; using System.Collections.Generic; using System.Data.SqlTypes; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace BLToolkit.Mapping { using DataAccess; using Reflection; using Reflection.Extension; using Reflection.MetadataProvider; [DebuggerDisplay("Type = {TypeAccessor.Type}, OriginalType = {TypeAccessor.OriginalType}")] public class ObjectMapper : MapDataSourceDestinationBase, IEnumerable<MemberMapper> { #region Protected Members protected virtual MemberMapper CreateMemberMapper(MapMemberInfo mapMemberInfo) { if (mapMemberInfo == null) throw new ArgumentNullException("mapMemberInfo"); MemberMapper mm = null; var attr = mapMemberInfo.MemberAccessor.GetAttribute<MemberMapperAttribute>(); MemberExtension ext; if (_extension != null && _extension.Members.TryGetValue(mapMemberInfo.MemberName,out ext)) { AttributeExtensionCollection attrExt; if (ext.Attributes.TryGetValue("MemberMapper", out attrExt)) { attr = new MemberMapperAttribute((Type)attrExt[0].Values["MemberMapperType"]); } } if (attr == null) { var attrs = TypeHelper.GetAttributes(mapMemberInfo.Type, typeof(MemberMapperAttribute)); foreach (MemberMapperAttribute a in attrs) { if (a.MemberType == null) { mm = a.MemberMapper; break; } } } else mm = attr.MemberMapper; if (mm == null) { var attrs = TypeHelper.GetAttributes( mapMemberInfo.MemberAccessor.MemberInfo.DeclaringType, typeof(MemberMapperAttribute)); foreach (MemberMapperAttribute a in attrs) { if (a.MemberType == mapMemberInfo.Type) { mm = a.MemberMapper; break; } } } if (mm == null) mm = MemberMapper.CreateMemberMapper(mapMemberInfo); mm.Init(mapMemberInfo); return mm; } [SuppressMessage("Microsoft.Performance", "CA1807:AvoidUnnecessaryStringCreation", MessageId = "stack1")] protected virtual void Add(MemberMapper memberMapper) { if (memberMapper == null) throw new ArgumentNullException("memberMapper"); memberMapper.SetOrdinal(_members.Count); _members .Add(memberMapper); _nameToMember .Add(memberMapper.Name.ToLower(), memberMapper); _memberNameToMember.Add(memberMapper.MemberName, memberMapper); } protected virtual MetadataProviderBase CreateMetadataProvider() { return MetadataProviderBase.CreateProvider(); } #endregion #region Public Members private readonly List<MemberMapper> _members = new List<MemberMapper>(); public MemberMapper this[int index] { get { return _members[index]; } } readonly List<Association> _associations = new List<Association>(); public List<Association> Associations { get { return _associations; } } readonly List<InheritanceMappingAttribute> _inheritanceMapping = new List<InheritanceMappingAttribute>(); public List<InheritanceMappingAttribute> InheritanceMapping { get { return _inheritanceMapping; } } [CLSCompliant(false)] protected TypeExtension _extension; public TypeExtension Extension { get { return _extension; } set { _extension = value; } } private MetadataProviderBase _metadataProvider; public MetadataProviderBase MetadataProvider { get { return _metadataProvider ?? (_metadataProvider = CreateMetadataProvider()); } set { _metadataProvider = value; } } private string[] _fieldNames; public string[] FieldNames { get { if (_fieldNames == null) { _fieldNames = new string[_members.Count]; for (var i = 0; i < _fieldNames.Length; i++) { _fieldNames[i] = _members[i].Name; } } return _fieldNames; } } private readonly Dictionary<string,MemberMapper> _nameToMember = new Dictionary<string,MemberMapper>(); private readonly Dictionary<string,MemberMapper> _memberNameToMember = new Dictionary<string,MemberMapper>(); public MemberMapper this[string name] { get { if (name == null) throw new ArgumentNullException("name"); lock (_nameToMember) { MemberMapper mm; if (!_nameToMember.TryGetValue(name, out mm)) { if (!_nameToMember.TryGetValue(name.ToLower(CultureInfo.CurrentCulture), out mm)) { lock (_memberNameToMember) if (_memberNameToMember.ContainsKey(name) || _memberNameToMember.ContainsKey(name.ToLower(CultureInfo.CurrentCulture))) return null; mm = GetComplexMapper(name, name); if (mm != null) { if (_members.Contains(mm)) { //throw new MappingException(string.Format( // "Wrong mapping field name: '{0}', type: '{1}'. Use field name '{2}' instead.", // name, _typeAccessor.OriginalType.Name, mm.Name)); return null; } Add(mm); } } else _nameToMember.Add(name, mm); } return mm; } } } public MemberMapper this[string name, bool byPropertyName] { get { MemberMapper mm; if (byPropertyName) lock (_memberNameToMember) return _memberNameToMember.TryGetValue(name, out mm) ? mm : null; return this[name]; } } public int GetOrdinal(string name, bool byPropertyName) { if (byPropertyName) { for (var i = 0; i < _members.Count; ++i) if (_members[i].MemberName == name) return i; return -1; } return GetOrdinal(name); } [CLSCompliant(false)] protected TypeAccessor _typeAccessor; public TypeAccessor TypeAccessor { get { return _typeAccessor; } } private MappingSchema _mappingSchema; public MappingSchema MappingSchema { get { return _mappingSchema; } } #endregion #region Init Mapper public virtual void Init(MappingSchema mappingSchema, Type type) { if (type == null) throw new ArgumentNullException("type"); _typeAccessor = TypeAccessor.GetAccessor(type); _mappingSchema = mappingSchema; _extension = TypeExtension.GetTypeExtension(_typeAccessor.OriginalType, mappingSchema.Extensions); _inheritanceMapping.AddRange(GetInheritanceMapping()); foreach (MemberAccessor ma in _typeAccessor) { var a = GetAssociation(ma); if (a != null) { _associations.Add(a); continue; } if (GetMapIgnore(ma)) continue; var mapFieldAttr = GetMapField(ma); // ma.GetAttribute<MapFieldAttribute>(); if (mapFieldAttr == null || (mapFieldAttr.OrigName == null && mapFieldAttr.Format == null)) { var mi = new MapMemberInfo(); var dbTypeAttribute = GetDbType(ma); // ma.GetAttribute<DbTypeAttribute>(); if (dbTypeAttribute != null) { mi.DbType = dbTypeAttribute.DbType; mi.IsDbTypeSet = true; if (dbTypeAttribute.Size != null) { mi.DbSize = dbTypeAttribute.Size.Value; mi.IsDbSizeSet = true; } } mi.MemberAccessor = ma; mi.Type = ma.Type; mi.MappingSchema = mappingSchema; mi.MemberExtension = _extension[ma.Name]; mi.Name = GetFieldName (ma); mi.MemberName = ma.Name; mi.Storage = GetFieldStorage(ma); mi.IsInheritanceDiscriminator = GetInheritanceDiscriminator(ma); mi.Trimmable = GetTrimmable (ma); mi.SqlIgnore = GetSqlIgnore (ma); mi.MapValues = GetMapValues (ma); mi.DefaultValue = GetDefaultValue(ma); mi.Nullable = GetNullable (ma); mi.NullValue = GetNullValue (ma, mi.Nullable); Add(CreateMemberMapper(mi)); } else if (mapFieldAttr.OrigName != null) { EnsureMapper(mapFieldAttr.MapName, ma.Name + "." + mapFieldAttr.OrigName); } else //if (mapFieldAttr.Format != null) { foreach (MemberMapper inner in _mappingSchema.GetObjectMapper(ma.Type)) EnsureMapper(string.Format(mapFieldAttr.Format, inner.Name), ma.Name + "." + inner.MemberName); } } foreach (var ae in _extension.Attributes["MapField"]) { var mapName = (string)ae["MapName"]; var origName = (string)ae["OrigName"]; if (mapName == null || origName == null) throw new MappingException(string.Format( "Type '{0}' has invalid extension. MapField MapName='{1}' OrigName='{2}'.", type.FullName, mapName, origName)); EnsureMapper(mapName, origName); } MetadataProvider.EnsureMapper(TypeAccessor, MappingSchema, EnsureMapper); } private MemberMapper EnsureMapper(string mapName, string origName) { var mm = this[mapName]; if (mm == null) { var name = mapName.ToLower(); foreach (var m in _members) { if (m.MemberAccessor.Name.ToLower() == name) { _nameToMember.Add(name, m); return m; } } mm = GetComplexMapper(mapName, origName); if (mm != null) Add(mm); } return mm; } private readonly Dictionary<string,MemberMapper> _nameToComplexMapper = new Dictionary<string,MemberMapper>(); [SuppressMessage("Microsoft.Performance", "CA1807:AvoidUnnecessaryStringCreation", MessageId = "stack0")] [SuppressMessage("Microsoft.Performance", "CA1807:AvoidUnnecessaryStringCreation", MessageId = "origName")] protected MemberMapper GetComplexMapper(string mapName, string origName) { if (origName == null) throw new ArgumentNullException("origName"); var name = origName.ToLower(); var idx = origName.IndexOf('.'); lock (_nameToComplexMapper) { MemberMapper mm; if (_nameToComplexMapper.TryGetValue(name, out mm)) return mm; if (idx > 0) { name = name.Substring(0, idx); foreach (MemberAccessor ma in TypeAccessor) { if (ma.Name.Length == name.Length && ma.Name.ToLower() == name) { var om = MappingSchema.GetObjectMapper(ma.Type); if (om != null) { mm = om.GetComplexMapper(mapName, origName.Substring(idx + 1)); if (mm != null) { var mi = new MapMemberInfo { MemberAccessor = ma, ComplexMemberAccessor = mm.ComplexMemberAccessor, Type = mm.Type, MappingSchema = MappingSchema, Name = mapName, MemberName = origName }; var mapper = new MemberMapper.ComplexMapper(mm); var key = origName.ToLower(); mapper.Init(mi); if (_nameToComplexMapper.ContainsKey(key)) _nameToComplexMapper[key] = mapper; else _nameToComplexMapper.Add(key, mapper); return mapper; } } break; } } } else { foreach (var m in _members) if (m.MemberAccessor.Name.Length == name.Length && m.MemberAccessor.Name.ToLower() == name) { if (_nameToComplexMapper.ContainsKey(name)) _nameToComplexMapper[name] = m; else _nameToComplexMapper.Add(name, m); return m; } } // Under some conditions, this way lead to memory leaks. // In other hand, shaking mappers up every time lead to performance loss. // So we cache failed requests. // If this optimization is a memory leak for you, just comment out next line. // if (_nameToComplexMapper.ContainsKey(name)) _nameToComplexMapper[name] = null; else _nameToComplexMapper.Add(name, null); return null; } } private MapValue[] GetMapValues(MemberAccessor member) { bool isSet; var values = MetadataProvider.GetMapValues(Extension, member, out isSet); return isSet? values: _mappingSchema.GetMapValues(member.Type); } protected virtual object GetDefaultValue(MemberAccessor memberAccessor) { bool isSet; var value = MetadataProvider.GetDefaultValue(MappingSchema, Extension, memberAccessor, out isSet); return isSet? value: _mappingSchema.GetDefaultValue(memberAccessor.Type); } protected virtual bool GetNullable(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetNullable(MappingSchema, Extension, memberAccessor, out isSet); } protected virtual bool GetLazyInstance(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetLazyInstance(MappingSchema, Extension, memberAccessor, out isSet); } protected virtual bool GetMapIgnore(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetMapIgnore(Extension, memberAccessor, out isSet); } protected virtual MapFieldAttribute GetMapField(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetMapField(Extension, memberAccessor, out isSet); } [CLSCompliant(false)] protected virtual DbTypeAttribute GetDbType(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetDbType(Extension, memberAccessor, out isSet); } protected virtual PrimaryKeyAttribute GetPrimaryKey(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetPrimaryKey(Extension, memberAccessor, out isSet); } protected virtual bool GetSqlIgnore(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetSqlIgnore(Extension, memberAccessor, out isSet); } protected virtual string GetFieldName(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetFieldName(Extension, memberAccessor, out isSet); } protected virtual string GetFieldStorage(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetFieldStorage(Extension, memberAccessor, out isSet); } protected virtual bool GetInheritanceDiscriminator(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetInheritanceDiscriminator(Extension, memberAccessor, out isSet); } protected virtual bool GetTrimmable(MemberAccessor memberAccessor) { bool isSet; return MetadataProvider.GetTrimmable(Extension, memberAccessor, out isSet); } protected virtual object GetNullValue(MemberAccessor memberAccessor, bool isNullable) { if (isNullable) { bool isSet; return MetadataProvider.GetNullValue(MappingSchema, Extension, memberAccessor, out isSet); } return MappingSchema.GetNullValue(memberAccessor.Type); } protected virtual Association GetAssociation(MemberAccessor memberAccessor) { return MetadataProvider.GetAssociation(Extension, memberAccessor); } protected virtual InheritanceMappingAttribute[] GetInheritanceMapping() { return MetadataProvider.GetInheritanceMapping(_typeAccessor.OriginalType, Extension); } #endregion #region IObjectMappper Members public virtual object CreateInstance() { return _typeAccessor.CreateInstanceEx(); } public virtual object CreateInstance(InitContext context) { return _typeAccessor.CreateInstanceEx(context); } #endregion #region IMapDataSource Members public override int Count { get { return _members.Count; } } public override Type GetFieldType(int index) { return _members[index].Type; } public override string GetName(int index) { return _members[index].Name; } public override object GetValue(object o, int index) { return _members[index].GetValue(o); } public override object GetValue(object o, string name) { MemberMapper mm; lock (_nameToMember) if (!_nameToMember.TryGetValue(name, out mm)) mm = this[name]; return mm == null? null: mm.GetValue(o); } public override bool IsNull (object o, int index) { return this[index].IsNull(o); } public override bool SupportsTypedValues(int index) { return this[index].SupportsValue; } // Simple type getters. // [CLSCompliant(false)] public override SByte GetSByte (object o, int index) { return this[index].GetSByte (o); } public override Int16 GetInt16 (object o, int index) { return this[index].GetInt16 (o); } public override Int32 GetInt32 (object o, int index) { return this[index].GetInt32 (o); } public override Int64 GetInt64 (object o, int index) { return this[index].GetInt64 (o); } public override Byte GetByte (object o, int index) { return this[index].GetByte (o); } [CLSCompliant(false)] public override UInt16 GetUInt16 (object o, int index) { return this[index].GetUInt16 (o); } [CLSCompliant(false)] public override UInt32 GetUInt32 (object o, int index) { return this[index].GetUInt32 (o); } [CLSCompliant(false)] public override UInt64 GetUInt64 (object o, int index) { return this[index].GetUInt64 (o); } public override Boolean GetBoolean (object o, int index) { return this[index].GetBoolean (o); } public override Char GetChar (object o, int index) { return this[index].GetChar (o); } public override Single GetSingle (object o, int index) { return this[index].GetSingle (o); } public override Double GetDouble (object o, int index) { return this[index].GetDouble (o); } public override Decimal GetDecimal (object o, int index) { return this[index].GetDecimal (o); } public override Guid GetGuid (object o, int index) { return this[index].GetGuid (o); } public override DateTime GetDateTime(object o, int index) { return this[index].GetDateTime(o); } public override DateTimeOffset GetDateTimeOffset(object o, int index) { return this[index].GetDateTimeOffset(o); } // Nullable type getters. // [CLSCompliant(false)] public override SByte? GetNullableSByte (object o, int index) { return this[index].GetNullableSByte (o); } public override Int16? GetNullableInt16 (object o, int index) { return this[index].GetNullableInt16 (o); } public override Int32? GetNullableInt32 (object o, int index) { return this[index].GetNullableInt32 (o); } public override Int64? GetNullableInt64 (object o, int index) { return this[index].GetNullableInt64 (o); } public override Byte? GetNullableByte (object o, int index) { return this[index].GetNullableByte (o); } [CLSCompliant(false)] public override UInt16? GetNullableUInt16 (object o, int index) { return this[index].GetNullableUInt16 (o); } [CLSCompliant(false)] public override UInt32? GetNullableUInt32 (object o, int index) { return this[index].GetNullableUInt32 (o); } [CLSCompliant(false)] public override UInt64? GetNullableUInt64 (object o, int index) { return this[index].GetNullableUInt64 (o); } public override Boolean? GetNullableBoolean (object o, int index) { return this[index].GetNullableBoolean (o); } public override Char? GetNullableChar (object o, int index) { return this[index].GetNullableChar (o); } public override Single? GetNullableSingle (object o, int index) { return this[index].GetNullableSingle (o); } public override Double? GetNullableDouble (object o, int index) { return this[index].GetNullableDouble (o); } public override Decimal? GetNullableDecimal (object o, int index) { return this[index].GetNullableDecimal (o); } public override Guid? GetNullableGuid (object o, int index) { return this[index].GetNullableGuid (o); } public override DateTime? GetNullableDateTime(object o, int index) { return this[index].GetNullableDateTime(o); } public override DateTimeOffset? GetNullableDateTimeOffset(object o, int index) { return this[index].GetNullableDateTimeOffset(o); } #if !SILVERLIGHT // SQL type getters. // public override SqlByte GetSqlByte (object o, int index) { return this[index].GetSqlByte (o); } public override SqlInt16 GetSqlInt16 (object o, int index) { return this[index].GetSqlInt16 (o); } public override SqlInt32 GetSqlInt32 (object o, int index) { return this[index].GetSqlInt32 (o); } public override SqlInt64 GetSqlInt64 (object o, int index) { return this[index].GetSqlInt64 (o); } public override SqlSingle GetSqlSingle (object o, int index) { return this[index].GetSqlSingle (o); } public override SqlBoolean GetSqlBoolean (object o, int index) { return this[index].GetSqlBoolean (o); } public override SqlDouble GetSqlDouble (object o, int index) { return this[index].GetSqlDouble (o); } public override SqlDateTime GetSqlDateTime(object o, int index) { return this[index].GetSqlDateTime(o); } public override SqlDecimal GetSqlDecimal (object o, int index) { return this[index].GetSqlDecimal (o); } public override SqlMoney GetSqlMoney (object o, int index) { return this[index].GetSqlMoney (o); } public override SqlGuid GetSqlGuid (object o, int index) { return this[index].GetSqlGuid (o); } public override SqlString GetSqlString (object o, int index) { return this[index].GetSqlString (o); } #endif #endregion #region IMapDataDestination Members public override int GetOrdinal(string name) { MemberMapper mm; lock (_nameToMember) if (!_nameToMember.TryGetValue(name, out mm)) mm = this[name]; return mm == null? -1: mm.Ordinal; } public override void SetValue(object o, int index, object value) { _members[index].SetValue(o, value); } public override void SetValue(object o, string name, object value) { SetValue(o, GetOrdinal(name), value); } public override void SetNull (object o, int index) { this[index].SetNull (o); } // Simple types setters. // [CLSCompliant(false)] public override void SetSByte (object o, int index, SByte value) { this[index].SetSByte (o, value); } public override void SetInt16 (object o, int index, Int16 value) { this[index].SetInt16 (o, value); } public override void SetInt32 (object o, int index, Int32 value) { this[index].SetInt32 (o, value); } public override void SetInt64 (object o, int index, Int64 value) { this[index].SetInt64 (o, value); } public override void SetByte (object o, int index, Byte value) { this[index].SetByte (o, value); } [CLSCompliant(false)] public override void SetUInt16 (object o, int index, UInt16 value) { this[index].SetUInt16 (o, value); } [CLSCompliant(false)] public override void SetUInt32 (object o, int index, UInt32 value) { this[index].SetUInt32 (o, value); } [CLSCompliant(false)] public override void SetUInt64 (object o, int index, UInt64 value) { this[index].SetUInt64 (o, value); } public override void SetBoolean (object o, int index, Boolean value) { this[index].SetBoolean (o, value); } public override void SetChar (object o, int index, Char value) { this[index].SetChar (o, value); } public override void SetSingle (object o, int index, Single value) { this[index].SetSingle (o, value); } public override void SetDouble (object o, int index, Double value) { this[index].SetDouble (o, value); } public override void SetDecimal (object o, int index, Decimal value) { this[index].SetDecimal (o, value); } public override void SetGuid (object o, int index, Guid value) { this[index].SetGuid (o, value); } public override void SetDateTime(object o, int index, DateTime value) { this[index].SetDateTime(o, value); } public override void SetDateTimeOffset(object o, int index, DateTimeOffset value) { this[index].SetDateTimeOffset(o, value); } // Simple types setters. // [CLSCompliant(false)] public override void SetNullableSByte (object o, int index, SByte? value) { this[index].SetNullableSByte (o, value); } public override void SetNullableInt16 (object o, int index, Int16? value) { this[index].SetNullableInt16 (o, value); } public override void SetNullableInt32 (object o, int index, Int32? value) { this[index].SetNullableInt32 (o, value); } public override void SetNullableInt64 (object o, int index, Int64? value) { this[index].SetNullableInt64 (o, value); } public override void SetNullableByte (object o, int index, Byte? value) { this[index].SetNullableByte (o, value); } [CLSCompliant(false)] public override void SetNullableUInt16 (object o, int index, UInt16? value) { this[index].SetNullableUInt16 (o, value); } [CLSCompliant(false)] public override void SetNullableUInt32 (object o, int index, UInt32? value) { this[index].SetNullableUInt32 (o, value); } [CLSCompliant(false)] public override void SetNullableUInt64 (object o, int index, UInt64? value) { this[index].SetNullableUInt64 (o, value); } public override void SetNullableBoolean (object o, int index, Boolean? value) { this[index].SetNullableBoolean (o, value); } public override void SetNullableChar (object o, int index, Char? value) { this[index].SetNullableChar (o, value); } public override void SetNullableSingle (object o, int index, Single? value) { this[index].SetNullableSingle (o, value); } public override void SetNullableDouble (object o, int index, Double? value) { this[index].SetNullableDouble (o, value); } public override void SetNullableDecimal (object o, int index, Decimal? value) { this[index].SetNullableDecimal (o, value); } public override void SetNullableGuid (object o, int index, Guid? value) { this[index].SetNullableGuid (o, value); } public override void SetNullableDateTime(object o, int index, DateTime? value) { this[index].SetNullableDateTime(o, value); } public override void SetNullableDateTimeOffset(object o, int index, DateTimeOffset? value) { this[index].SetNullableDateTimeOffset(o, value); } #if !SILVERLIGHT // SQL type setters. // public override void SetSqlByte (object o, int index, SqlByte value) { this[index].SetSqlByte (o, value); } public override void SetSqlInt16 (object o, int index, SqlInt16 value) { this[index].SetSqlInt16 (o, value); } public override void SetSqlInt32 (object o, int index, SqlInt32 value) { this[index].SetSqlInt32 (o, value); } public override void SetSqlInt64 (object o, int index, SqlInt64 value) { this[index].SetSqlInt64 (o, value); } public override void SetSqlSingle (object o, int index, SqlSingle value) { this[index].SetSqlSingle (o, value); } public override void SetSqlBoolean (object o, int index, SqlBoolean value) { this[index].SetSqlBoolean (o, value); } public override void SetSqlDouble (object o, int index, SqlDouble value) { this[index].SetSqlDouble (o, value); } public override void SetSqlDateTime(object o, int index, SqlDateTime value) { this[index].SetSqlDateTime(o, value); } public override void SetSqlDecimal (object o, int index, SqlDecimal value) { this[index].SetSqlDecimal (o, value); } public override void SetSqlMoney (object o, int index, SqlMoney value) { this[index].SetSqlMoney (o, value); } public override void SetSqlGuid (object o, int index, SqlGuid value) { this[index].SetSqlGuid (o, value); } public override void SetSqlString (object o, int index, SqlString value) { this[index].SetSqlString (o, value); } #endif #endregion #region IEnumerable Members public IEnumerator GetEnumerator() { return _members.GetEnumerator(); } IEnumerator<MemberMapper> IEnumerable<MemberMapper>.GetEnumerator() { return _members.GetEnumerator(); } #endregion } }