Skip to content

Commit

Permalink
added lazy loading functionality Node Mapper using Castle Dynamic proxy.
Browse files Browse the repository at this point in the history
  • Loading branch information
Sony Arouje committed Feb 21, 2012
1 parent d916c92 commit df8c77d
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 26 deletions.
Binary file added Lib/Castle.Core.dll
Binary file not shown.
2 changes: 1 addition & 1 deletion Net.Graph.Neo4JD/EntityMapper/EntityId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Net.Graph.Neo4JD.EntityMapper
{
[System.AttributeUsage(System.AttributeTargets.Property)]
[System.AttributeUsage(System.AttributeTargets.Property|System.AttributeTargets.Method)]
public class EntityId : System.Attribute
{

Expand Down
54 changes: 54 additions & 0 deletions Net.Graph.Neo4JD/EntityMapper/LazyLoadInterceptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Reflection;
using Castle.Core;
using Castle.DynamicProxy;
using Castle.Core.Internal;

namespace Net.Graph.Neo4JD.EntityMapper
{
public class LazyLoadInterceptor:IInterceptor
{
public void Intercept(IInvocation invocation)
{
bool isIntercepted = false;
string propName = invocation.Method.Name.Replace("get_", "");

if (invocation.Method.Name.StartsWith("get_", StringComparison.OrdinalIgnoreCase))
{
Type type = invocation.TargetType.GetProperty(propName).PropertyType;
if (MapperHelper.IsPrimitive(type) == false)
{
isIntercepted = true;
invocation.Proceed();
if (invocation.ReturnValue == null)
invocation.ReturnValue = this.DoLazyLoad(type, invocation.Proxy, propName);

Console.WriteLine(type.ToString());
}
}
if (isIntercepted == false)
invocation.Proceed();
}

private object DoLazyLoad(Type type, object entity, string propertyName)
{
var castedEntity = entity as IProxyTargetAccessor;
Type genericType = type.IsGenericType ? type.GetGenericArguments()[0] : type;
MethodInfo method = typeof(NodeMapper).GetMethod("LoadRelatedEntitiesWithId");
MethodInfo generic = method.MakeGenericMethod(genericType);
int id = MapperHelper.GetIdentity(castedEntity.DynProxyGetTarget());

object result = generic.Invoke(null, new object[] { id, propertyName });

if (type.Namespace != "System.Collections.Generic")
result= ((IEnumerable)result).Cast<object>().ToArray().FirstOrDefault();

return result;
}

}
}
18 changes: 18 additions & 0 deletions Net.Graph.Neo4JD/EntityMapper/LazyLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.DynamicProxy;
namespace Net.Graph.Neo4JD.EntityMapper
{
public static class LazyLoader
{
private static readonly ProxyGenerator _generator=new ProxyGenerator();
public static T EnableLazyLoading<T>() where T:class
{
LazyLoadInterceptor interceptor = new LazyLoadInterceptor();
var proxy = _generator.CreateClassProxy<T>(interceptor);
return proxy;
}
}
}
5 changes: 5 additions & 0 deletions Net.Graph.Neo4JD/EntityMapper/MapperHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ internal static object SetIdentity(object entity, int id)

internal static int GetIdentity<T>(T entity) where T : class
{
if (entity == null)
return 0;
object propertyValue = 0;
typeof(T).GetProperties().Where(pr => pr.CanRead && IsAnId(pr) == true).ToList().ForEach(property =>
{
Expand All @@ -40,6 +42,9 @@ internal static int GetIdentity<T>(T entity) where T : class

internal static int GetIdentity(object entity)
{
if (entity == null)
return 0;

object propertyValue = 0;
entity.GetType().GetProperties().Where(pr => pr.CanRead && IsAnId(pr) == true).ToList().ForEach(property =>
{
Expand Down
37 changes: 20 additions & 17 deletions Net.Graph.Neo4JD/EntityMapper/NodeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,12 @@ public class NodeMapper
public T Get<T>(int id) where T:class
{
Node node = Node.Get(id);
T entity = (T)Activator.CreateInstance(typeof(T));
if (node.GetProperty("clazz")!=typeof(T).ToString())
throw new InvalidCastException(string.Format("Retrieved object with ID '{0}' is an instance of '{1}' and unable to cast it to '{2}'", id.ToString(), node.GetProperty("clazz"), typeof(T).ToString()));
typeof(T).GetProperties().Where(pr => pr.CanRead && MapperHelper.IsAnId(pr) == false).ToList().ForEach(property =>
{
property.SetValue(entity, MapperHelper.CastPropertyValue(property, node.GetProperty(property.Name)), null);
});

entity = MapperHelper.SetIdentity<T>(entity, id);
return entity;
return Get<T>(node);
}

private T Get<T>(Node node)
private static T Get<T>(Node node) where T:class
{
T entity = (T)Activator.CreateInstance(typeof(T));
T entity = LazyLoader.EnableLazyLoading<T>(); //(T)Activator.CreateInstance(typeof(T));
if (node.GetProperty("clazz") != typeof(T).ToString())
throw new InvalidCastException (string.Format("Retrieved object with ID '{0}' is an instance of '{1}' and unable to cast it to '{2}'",node.Id.ToString(), node.GetProperty("clazz"), typeof(T).ToString()));
typeof(T).GetProperties().Where(pr => pr.CanRead && MapperHelper.IsAnId(pr) == false).ToList().ForEach(property =>
Expand Down Expand Up @@ -159,29 +150,41 @@ public IList<TRelated> GetRelatedEntities<TEntity, TRelated>(TEntity entity, Typ
IList<Node> relatedNodes = node.Filter(germlin);
IList<TRelated> relatedEntities = new List<TRelated>();
foreach (Node nodeToConvert in relatedNodes)
relatedEntities.Add(this.Get<TRelated>(nodeToConvert));
relatedEntities.Add(Get<TRelated>(nodeToConvert));
return relatedEntities;
}

public IList<TRelated> GetRelatedEntities<TRelated>(Expression<Func<TRelated>> property, object entity)
public IList<TRelated> GetRelatedEntities<TRelated>(object entity,Expression<Func<TRelated>> property) where TRelated:class
{
return this.LoadRelatedEntities<TRelated>(entity, ((MemberExpression)property.Body).Member.Name);
}

public IList<TRelated> GetRelatedEntities<TRelated>(Expression<Func<ICollection<TRelated>>> property, object entity)
public IList<TRelated> GetRelatedEntities<TRelated>(object entity,Expression<Func<ICollection<TRelated>>> property) where TRelated : class
{
return this.LoadRelatedEntities<TRelated>(entity, ((MemberExpression)property.Body).Member.Name);
}

private IList<TRelated> LoadRelatedEntities<TRelated>(object entity, string memberName)
public IList<TRelated> LoadRelatedEntities<TRelated>(object entity, string memberName) where TRelated : class
{
Node node = Node.Get(MapperHelper.GetIdentity(entity));
GermlinPipe germlin = new GermlinPipe();
germlin.G.V.Out(memberName);
IList<Node> relatedNodes = node.Filter(germlin);
IList<TRelated> relatedEntities = new List<TRelated>();
foreach (Node nodeToConvert in relatedNodes)
relatedEntities.Add(this.Get<TRelated>(nodeToConvert));
relatedEntities.Add(Get<TRelated>(nodeToConvert));
return relatedEntities;
}

public static IList<TRelated> LoadRelatedEntitiesWithId<TRelated>(int id, string memberName) where TRelated : class
{
Node node = Node.Get(id);
GermlinPipe germlin = new GermlinPipe();
germlin.G.V.Out(memberName);
IList<Node> relatedNodes = node.Filter(germlin);
IList<TRelated> relatedEntities = new List<TRelated>();
foreach (Node nodeToConvert in relatedNodes)
relatedEntities.Add(Get<TRelated>(nodeToConvert));
return relatedEntities;
}
}
Expand Down
5 changes: 5 additions & 0 deletions Net.Graph.Neo4JD/Net.Graph.Neo4JD.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Castle.Core">
<HintPath>..\Lib\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\Lib\Newtonsoft.Json.dll</HintPath>
</Reference>
Expand All @@ -48,6 +51,8 @@
<Compile Include="EntityMapper\EntityConfiguration.cs" />
<Compile Include="EntityMapper\EntityId.cs" />
<Compile Include="EntityMapper\EntityRelation.cs" />
<Compile Include="EntityMapper\LazyLoader.cs" />
<Compile Include="EntityMapper\LazyLoadInterceptor.cs" />
<Compile Include="EntityMapper\MapperHelper.cs" />
<Compile Include="EntityMapper\ModelBuilder.cs" />
<Compile Include="EntityMapper\NodeMapper.cs" />
Expand Down
6 changes: 6 additions & 0 deletions Net.Graph.Neo4JD/Net.Graph.Neo4JD.csproj.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectView>ProjectFiles</ProjectView>
</PropertyGroup>
</Project>
Binary file modified Net.Graph.Neo4jD.sln.docstates.suo
Binary file not shown.
Binary file modified Net.Graph.Neo4jD.suo
Binary file not shown.
30 changes: 22 additions & 8 deletions Test.Neo4JD/EntityMappingTest/ComplexObjectGraphMappingTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,14 @@ public Order()

[EntityId]
public int Id { get; set; }
public string Name { get; set; }
public virtual string Name { get; set; }

IList<OrderItem> _orderItems;
public IList<OrderItem> OrderItems
public virtual IList<OrderItem> OrderItems
{
get
{
if (_orderItems.Count > 0)
return _orderItems;

NodeMapper nodeMapper = new NodeMapper();
return nodeMapper.GetRelatedEntities<OrderItem>(() => OrderItems, this);
return _orderItems;
}
private set
{
Expand All @@ -55,7 +51,8 @@ public OrderItem(int id, Product product)
}
[EntityId]
public int Id { get; set; }
public Product Product { get; set; }

public virtual Product Product { get; set; }
}

public class Product
Expand Down Expand Up @@ -117,5 +114,22 @@ public void GetComplexObject()
Assert.AreEqual(14, order.Id);
Assert.AreEqual(2, order.OrderItems.Count);
}

[TestCase]
public void GetOrder()
{
NodeMapper nodeMapper = new NodeMapper();
Order order = nodeMapper.Get<Order>(14);
Assert.AreEqual(14, order.Id);
foreach (OrderItem item in order.OrderItems)
{
Console.WriteLine(item.Id.ToString());
Product prod = item.Product;
if (prod != null)
Console.WriteLine(prod.ProductName);
}
//Assert.AreEqual(null, order.OrderItems);

}
}
}

0 comments on commit df8c77d

Please sign in to comment.