using System ;
using System.Collections.Generic ;
using System.Linq.Expressions ;
using System.Reflection ;
namespace Marr.Data.Reflection
{
public class SimpleReflectionStrategy : IReflectionStrategy
{
private static readonly Dictionary < string , MemberInfo > MemberCache = new Dictionary < string , MemberInfo > ( ) ;
private static readonly Dictionary < string , GetterDelegate > GetterCache = new Dictionary < string , GetterDelegate > ( ) ;
private static readonly Dictionary < string , SetterDelegate > SetterCache = new Dictionary < string , SetterDelegate > ( ) ;
private static MemberInfo GetMember ( Type entityType , string name )
{
MemberInfo member ;
var key = entityType . FullName + name ;
if ( ! MemberCache . TryGetValue ( key , out member ) )
{
member = entityType . GetMember ( name , BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance ) [ 0 ] ;
MemberCache [ key ] = member ;
}
return member ;
}
/// <summary>
/// Gets an entity field value by name.
/// </summary>
public object GetFieldValue ( object entity , string fieldName )
{
var member = GetMember ( entity . GetType ( ) , fieldName ) ;
if ( member . MemberType = = MemberTypes . Field )
{
return ( member as FieldInfo ) . GetValue ( entity ) ;
}
if ( member . MemberType = = MemberTypes . Property )
{
return BuildGetter ( entity . GetType ( ) , fieldName ) ( entity ) ;
}
throw new DataMappingException ( string . Format ( "The DataMapper could not get the value for {0}.{1}." , entity . GetType ( ) . Name , fieldName ) ) ;
}
/// <summary>
/// Instantiates a type using the FastReflector library for increased speed.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public object CreateInstance ( Type type )
{
return Activator . CreateInstance ( type ) ;
}
public GetterDelegate BuildGetter ( Type type , string memberName )
{
GetterDelegate getter ;
var key = type . FullName + memberName ;
if ( ! GetterCache . TryGetValue ( key , out getter ) )
{
getter = GetPropertyGetter ( ( PropertyInfo ) GetMember ( type , memberName ) ) ;
}
return getter ;
}
public SetterDelegate BuildSetter ( Type type , string memberName )
{
SetterDelegate setter ;
var key = type . FullName + memberName ;
if ( ! SetterCache . TryGetValue ( key , out setter ) )
{
setter = GetPropertySetter ( ( PropertyInfo ) GetMember ( type , memberName ) ) ;
}
return setter ;
}
private static SetterDelegate GetPropertySetter ( PropertyInfo propertyInfo )
{
var propertySetMethod = propertyInfo . GetSetMethod ( ) ;
if ( propertySetMethod = = null ) return null ;
#if NO_EXPRESSIONS
return ( o , convertedValue ) = >
{
propertySetMethod . Invoke ( o , new [ ] { convertedValue } ) ;
return ;
} ;
# else
var instance = Expression . Parameter ( typeof ( object ) , "i" ) ;
var argument = Expression . Parameter ( typeof ( object ) , "a" ) ;
var instanceParam = Expression . Convert ( instance , propertyInfo . DeclaringType ) ;
var valueParam = Expression . Convert ( argument , propertyInfo . PropertyType ) ;
var setterCall = Expression . Call ( instanceParam , propertyInfo . GetSetMethod ( ) , valueParam ) ;
return Expression . Lambda < SetterDelegate > ( setterCall , instance , argument ) . Compile ( ) ;
# endif
}
private static GetterDelegate GetPropertyGetter ( PropertyInfo propertyInfo )
{
var getMethodInfo = propertyInfo . GetGetMethod ( ) ;
if ( getMethodInfo = = null ) return null ;
#if NO_EXPRESSIONS
return o = > propertyInfo . GetGetMethod ( ) . Invoke ( o , new object [ ] { } ) ;
# else
try
{
var oInstanceParam = Expression . Parameter ( typeof ( object ) , "oInstanceParam" ) ;
var instanceParam = Expression . Convert ( oInstanceParam , propertyInfo . DeclaringType ) ;
var exprCallPropertyGetFn = Expression . Call ( instanceParam , getMethodInfo ) ;
var oExprCallPropertyGetFn = Expression . Convert ( exprCallPropertyGetFn , typeof ( object ) ) ;
var propertyGetFn = Expression . Lambda < GetterDelegate >
(
oExprCallPropertyGetFn ,
oInstanceParam
) . Compile ( ) ;
return propertyGetFn ;
}
catch ( Exception ex )
{
Console . Write ( ex . Message ) ;
throw ;
}
# endif
}
}
}