Skip to content

Commit

Permalink
Progress towards lazy loading: Unified member accessors.
Browse files Browse the repository at this point in the history
  • Loading branch information
ToddThomson committed Aug 23, 2018
1 parent 7a21ce8 commit bf12431
Show file tree
Hide file tree
Showing 24 changed files with 226 additions and 138 deletions.
2 changes: 1 addition & 1 deletion Achilles.Entities.Sqlite/DataContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public int Add<TEntity>( TEntity entity ) where TEntity : class
// TJT: Sqlite specific
var rowId = Database.Connection.LastInsertRowId();

entityMapping.SetPropertyValue( entity, key.PropertyName, rowId );
entityMapping.SetColumn( entity, key.PropertyName, rowId );
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

#endregion

namespace Achilles.Entities
namespace Achilles.Entities.Linq
{
public sealed class EntityCollection<TEntity> : IEntityCollection<TEntity>, IEntityCollection, ICollection<TEntity>, IListSource
where TEntity : class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
#region Namespaces

using System.Collections.Generic;
using System.Linq;

#endregion

namespace Achilles.Entities
namespace Achilles.Entities.Linq
{
public sealed class EntityReference<TEntity> : IEntityReference<TEntity>, IEntityReference
where TEntity : class
Expand Down Expand Up @@ -53,6 +54,7 @@ public TEntity Entity
}
}

// TJT: Make a direct internal property (Source) reference through a compiled lambda expression
public void AttachSource( IEnumerable<TEntity> source )
{
_source = source;
Expand All @@ -64,7 +66,11 @@ public void AttachSource( IEnumerable<TEntity> source )

private void Load()
{

if ( !_isLoaded && _source != null )
{
_entity = Enumerable.SingleOrDefault( _source );
_isLoaded = true;
}
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#endregion

namespace Achilles.Entities
namespace Achilles.Entities.Linq
{
/// <summary>
/// Marker interface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

#endregion

namespace Achilles.Entities
namespace Achilles.Entities.Linq
{
internal interface IEntityCollection<TEntity>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

using System.Collections.Generic;

namespace Achilles.Entities
namespace Achilles.Entities.Linq
{
/// <summary>
/// Marker interface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#endregion

namespace Achilles.Entities
namespace Achilles.Entities.Linq
{
public interface IEntityReference<TEntity>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#region Namespaces

using Achilles.Entities.Extensions;
using Achilles.Entities.Linq;
using Achilles.Entities.Reflection;
using System;
using System.Linq.Expressions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#region Namespaces

using Achilles.Entities.Extensions;
using Achilles.Entities.Linq;
using Achilles.Entities.Reflection;
using System;
using System.Collections;
Expand Down
98 changes: 98 additions & 0 deletions Achilles.Entities.Sqlite/Modelling/Mapping/ColumnAccessor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#region Copyright Notice

// Copyright (c) by Achilles Software, All rights reserved.
//
// Licensed under the MIT License. See License.txt in the project root for license information.
//
// Send questions regarding this copyright notice to: mailto:[email protected]

#endregion

#region Namespaces

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

#endregion

namespace Achilles.Entities.Modelling.Mapping
{
internal class ColumnAccessor<TEntity> : MemberAccessor
where TEntity : class
{
private readonly MemberInfo _columnInfo;

private Func<TEntity, object> _getter;
private Action<TEntity, object> _setter;

public ColumnAccessor( MemberInfo columnInfo )
: base( columnInfo )
{
_columnInfo = columnInfo;

CreateGetter();
CreateSetter();
}

public override object GetValue<TMember>( TMember entity ) => _getter( entity as TEntity );

public override void SetValue<TMember>( TMember entity, object value ) => _setter( entity as TEntity, value );

private void CreateGetter()
{
if ( _columnInfo is PropertyInfo propertyInfo )
{
ParameterExpression instance = Expression.Parameter( typeof( TEntity ), "instance" );

var body = Expression.Call( instance, propertyInfo.GetGetMethod() );
var parameters = new ParameterExpression[] { instance };
Expression conversion = Expression.Convert( body, typeof( object ) );

_getter = Expression.Lambda<Func<TEntity, object>>( conversion, parameters ).Compile();
}
else if ( _columnInfo is FieldInfo field )
{
ParameterExpression instance = Expression.Parameter( typeof( TEntity ), "instance" );

MemberExpression fieldExpression = Expression.Field( instance, field );
var parameters = new ParameterExpression[] { instance };
Expression conversion = Expression.Convert( fieldExpression, typeof( object ) );

_getter = Expression.Lambda<Func<TEntity, object>>( conversion, parameters ).Compile();
}
}

private void CreateSetter()
{
if ( _columnInfo is PropertyInfo propertyInfo )
{
var columnPropertySetMethod = propertyInfo.GetSetMethod();
var setMethodParameterType = columnPropertySetMethod.GetParameters().First().ParameterType;

var entityInstanceParameter = Expression.Parameter( typeof( TEntity ), "instance" );
var valueParameter = Expression.Parameter( typeof( object ), "value" );
Expression conversion = Expression.Convert( valueParameter, setMethodParameterType );

var body = Expression.Call( entityInstanceParameter, columnPropertySetMethod, conversion );
var parameters = new ParameterExpression[] { entityInstanceParameter, valueParameter };

_setter = Expression.Lambda<Action<TEntity, object>>(
body, parameters ).Compile();
}
else if ( _columnInfo is FieldInfo field )
{
var instanceParameter = Expression.Parameter( typeof( TEntity ), "instance" );
var valueParameter = Expression.Parameter( typeof( object ), "value" );
Expression conversion = Expression.Convert( valueParameter, field.FieldType );

MemberExpression fieldExpression = Expression.Field( instanceParameter, field );
BinaryExpression assignExp = Expression.Assign( fieldExpression, conversion );

_setter = Expression.Lambda<Action<TEntity, object>>(
assignExp, instanceParameter, valueParameter ).Compile();
}
}
}
}
83 changes: 16 additions & 67 deletions Achilles.Entities.Sqlite/Modelling/Mapping/EntityMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,14 @@ public class EntityMapping<TEntity> : IEntityMapping where TEntity : class
{
#region Fields

private Dictionary<string, MemberInfo> _columnMapping;
private Dictionary<string, MemberInfo> _columnProperties;
private Dictionary<string, MemberInfo> _foreignKeyProperties;
private Dictionary<string, MemberInfo> _relationshipProperties;

private Dictionary<string, Func<TEntity, object>> Getters = new Dictionary<string, Func<TEntity, object>>();
private Dictionary<string, Action<TEntity,object>> Setters = new Dictionary<string, Action<TEntity,object>>();
//private Dictionary<string, Func<TEntity, object>> ColumnGetters = new Dictionary<string, Func<TEntity, object>>();
//private Dictionary<string, Action<TEntity,object>> ColumnSetters = new Dictionary<string, Action<TEntity,object>>();

private Dictionary<string, MemberAccessor> ColumnAccessors = new Dictionary<string, MemberAccessor>();

#endregion

Expand All @@ -50,9 +54,9 @@ public EntityMapping()

#region Public Properties

public object GetPropertyValue<T>( T entity, string propertyName ) where T : class => Getters[ propertyName ].Invoke( entity as TEntity );
public object GetColumn<T>( T entity, string propertyName ) where T : class => ColumnAccessors[ propertyName ].GetValue( entity as TEntity );

public void SetPropertyValue<T>( T entity, string propertyName, object value ) where T: class => Setters[ propertyName ].Invoke( entity as TEntity, value );
public void SetColumn<T>( T entity, string propertyName, object value ) where T: class => ColumnAccessors[ propertyName ].SetValue( entity as TEntity, value );

public List<IColumnMapping> ColumnMappings { get; set; } = new List<IColumnMapping>();

Expand All @@ -76,82 +80,27 @@ public EntityMapping()

public void Compile()
{
_columnMapping = ColumnMappings.ToDictionary( m => m.ColumnName, m => m.ColumnInfo, IsCaseSensitive? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase );
_columnProperties = ColumnMappings.ToDictionary( m => m.ColumnName, m => m.ColumnInfo, IsCaseSensitive? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase );

CreateGetters();
CreateSetters();
CreateAccessors();
}

#endregion

#region Private Methods

private void CreateGetters()
private void CreateAccessors()
{
// Columns...
foreach ( var columnMapping in ColumnMappings )
{
if ( columnMapping.ColumnInfo is PropertyInfo propertyInfo )
{
ParameterExpression instance = Expression.Parameter( typeof( TEntity ), "instance" );

var body = Expression.Call( instance, propertyInfo.GetGetMethod() );
var parameters = new ParameterExpression[] { instance };
Expression conversion = Expression.Convert( body, typeof( object ) );

var getter = Expression.Lambda<Func<TEntity, object>>( conversion, parameters ).Compile();

Getters.Add( propertyInfo.Name, getter );
}
else if ( columnMapping.ColumnInfo is FieldInfo field )
{
ParameterExpression instance = Expression.Parameter( typeof( TEntity ), "instance" );

MemberExpression fieldExpression = Expression.Field( instance, field );
var parameters = new ParameterExpression[] { instance };
Expression conversion = Expression.Convert( fieldExpression, typeof( object ) );

var getter = Expression.Lambda<Func<TEntity, object>>( conversion, parameters ).Compile();

Getters.Add( field.Name, getter );
}
ColumnAccessors.Add( columnMapping.PropertyName, new ColumnAccessor<TEntity>( columnMapping.ColumnInfo ) );
}
}

private void CreateSetters()
{
foreach ( var columnMapping in ColumnMappings )
{
if ( columnMapping.ColumnInfo is PropertyInfo propertyInfo )
{
var setMethod = propertyInfo.GetSetMethod();
var setMethodParameterType = setMethod.GetParameters().First().ParameterType;

ParameterExpression instance = Expression.Parameter( typeof( TEntity ), "instance" );
var parameterExpression = Expression.Parameter( typeof( object ), "value" );
Expression conversion = Expression.Convert( parameterExpression, setMethodParameterType );

var body = Expression.Call( instance, setMethod, conversion );
var parameters = new ParameterExpression[] { instance, parameterExpression };

var setter = Expression.Lambda<Action<TEntity, object>>( body, parameters ).Compile();

Setters.Add( propertyInfo.Name, setter );
}
else if ( columnMapping.ColumnInfo is FieldInfo field )
{
ParameterExpression instance = Expression.Parameter( typeof( TEntity ), "instance" );
ParameterExpression valueExpression = Expression.Parameter( typeof( object ), "value" );
Expression conversion = Expression.Convert( valueExpression, field.FieldType );

MemberExpression fieldExpression = Expression.Field( instance, field );
BinaryExpression assignExp = Expression.Assign( fieldExpression, conversion );

var setter = Expression.Lambda<Action<TEntity, object>>
( assignExp, instance, valueExpression ).Compile();

Setters.Add( field.Name, setter );
}
}
private void CreateRelationshipSetters()
{
}

/// <summary>
Expand Down
Loading

0 comments on commit bf12431

Please sign in to comment.