尝试构建 Neo4j 存储库

问题描述 投票:0回答:1

在有人问之前,我确实知道已经有一个 Neo4jClient 存储库,但它已经过时,并且尚未针对 Neo4j 2.x 和新的 Neo4jClient 代码进行更新。

我的目标是实现这样的目标:

var profiles = profileRepository.Get((Data.Model.Profile profile) => profile.Age > 20);

我首先尝试通过将谓词传递给Where调用并在CypherFluentQuery上执行Return来手动构建表达式,但谓词参数与我在Return调用中的参数不匹配:

return client.Cypher
       .Match("(entity:" + _entityTypeName + ")")
       .Where(predicate)
       .Return(entity => entity.As<TEntity>)
       .Results.FirstOrDefault();

这就是我决定需要动态构建返回表达式的最终原因,以便它能够正确命名要传递给 Neo4jClient 的参数,并且不会返回未定义的异常。

我已经开始研究 Linq 表达式树和 lambda 表达式,以在 .NET 4.5 和 Neo4j 2.x 中构建基于谓词的通用 Neo4jClient 存储库。然而,我还不完全理解如何构建表达式树。这是我的 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 IEnumerable<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;
        }
    }
}

这是我的 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();
    }
}

当我尝试使用谓词执行 get 操作时,编译器不接受我的 lambda 表达式:

类型的参数表达式 'System.Linq.Expressions.Expression

1[System.Func
2[Neo4jClient.Cypher.ICypherResultItem,Data.Model.Profile]]' 不能用于类型的委托参数 'Neo4jClient.Cypher.ICypherResultItem'

如何根据输入谓词构建具有动态参数名称的表达式,该表达式适合像

entity => entity.As<TEntity>
这样的 Return 调用?

c# linq lambda neo4j neo4jclient
1个回答
3
投票

经过一段时间的修改,我想出了一个通用存储库: https://github.com/pcmantinker/Neo4jRepository

这是一些示例用法:
BlogPost.cs

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

namespace Neo4jRepository.Data.Model
{
    public class BlogPost : Neo4jEntity
    {
        public string Author { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public DateTimeOffset Created { get; set; }        

        public BlogPost()
        {
            Label = "BlogPost";
            Created = DateTimeOffset.UtcNow;
        }
    }
}

BlogPostRepository.cs

using Neo4jRepository.Data.Model;
using System.Threading.Tasks;

namespace Neo4jRepository.Repository
{
    public class BlogPostRepository : Neo4jRepository<BlogPost>
    {
        // any custom methods or overridden methods here

        /// <summary>
        /// Adds or updates a blog post.
        /// </summary>
        /// <param name="post">The post.</param>
        /// <returns></returns>
        public async Task AddOrUpdate(BlogPost post)
        {
            var found = await this.Single(p => p.Author == post.Author && p.Content == post.Content && p.Title == post.Title);
            if(found == null)
            {
                await Add(post);
            }
            else
            {
                await Update(p => p.Author == post.Author && p.Content == post.Content && p.Title == post.Title, post);
            }
        }
    }
}

示例程序:

BlogPostRepository _repo = new BlogPostRepository();
BlogPost post = new BlogPost() 
{
    Author = "John Smith",
    Title = "Hello Blog!",
    Content = "Test blog content"        
};
BlogPost post2 = new BlogPost() 
{
    Author = "Jane Smith",
    Title = "Hello Blog!",
    Content = "Test blog content"        
};
await _repo.AddOrUpdate(post);
await _repo.AddOrUpdate(post2);
IEnumerable<BlogPost> blogPosts = await _repo.All();
IEnumerable<BlogPost> janesPosts = await _repo.Where(b => b.Author == "Jane Smith");

实体必须继承自 Neo4jEntity 才能引用该实体节点上的标签。通过使存储库通用,我们可以轻松地为从 Neo4jRepository 继承的任何具有标准 CRUD 操作的存储库建立存储库。

© www.soinside.com 2019 - 2024. All rights reserved.