Before anyone asks, I do know that there is already a Neo4jClient repository, but it is dated and hasn't been updated for Neo4j 2.x and the new Neo4jClient code.
My goal is to achieve something like this:
var profiles = profileRepository.Get((Data.Model.Profile profile) => profile.Age > 20);
I first tried simply building the expression manually by passing the predicate to the Where call and executing Return on the CypherFluentQuery, but the predicate parameter didn't match what I had in the Return call:
return client.Cypher
.Match("(entity:" + _entityTypeName + ")")
.Where(predicate)
.Return(entity => entity.As<TEntity>)
.Results.FirstOrDefault();
This is ultimately why I decided that I need to build the return expression dynamically so that it would correctly name the parameters to pass to Neo4jClient and not come back with an undefined exception.
I've started researching Linq expression trees and lambda expressions to build a predicate based generic Neo4jClient repository in .NET 4.5 and Neo4j 2.x. I don't fully understand how to build an expression tree yet however. Here is my Neo4jRepository Get:
using Neo4jClient;
using Neo4jClient.Cypher;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace Data.Repository
{
public class Neo4jRepository<TEntity> : IRepository<TEntity>
{
private List<TEntity> _entities;
private string _entityTypeName;
public Neo4jRepository()
{
_entities = new List<TEntity>();
Type entityType = typeof(TEntity);
_entityTypeName = entityType.Name;
}
public virtual IEnumberable<TEntity> Get(Expression<Func<TEntity, bool>> predicate)
{
var client = new GraphClient(
new Uri("http://localhost:7474/db/data"));
client.Connect();
ParameterExpression parameter =
Expression.Parameter(typeof(
Expression<Func<ICypherResultItem, TEntity>>),
predicate.Parameters[0].Name);
var exp = Expression.Lambda<Func<ICypherResultItem, TEntity>>(
parameters: parameter,
body: Expression.Call(
instance: Expression.Default(typeof(ICypherResultItem)),
methodName: "As",
typeArguments: new[] { typeof(TEntity) }
)
);
return client.Cypher
.Match("(entity:" + _entityTypeName + ")")
.Where(predicate)
.Return(exp)
.Results;
}
}
}
Here's my IRepository:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace Data.Repository
{
/// <summary>
/// Generic repository that can be used with any data backend
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
interface IRepository<TEntity>
{
IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> predicate);
void Add(TEntity item);
void Update(Func<TEntity,bool> predicate, TEntity item);
void Delete(TEntity item);
void SaveChanges();
}
}
When I try to do a get with a predicate, the compiler doesn't accept my lambda expression:
ParameterExpression of type 'System.Linq.Expressions.Expression
1[System.Func
2[Neo4jClient.Cypher.ICypherResultItem,Data.Model.Profile]]' cannot be used for delegate parameter of type 'Neo4jClient.Cypher.ICypherResultItem'
How do I build an expression with a dynamic parameter name based on my input predicate that will fit into the Return call like entity => entity.As<TEntity>
?