首页 文章

试图 Build 一个Neo4j存储库

提问于
浏览
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,而不会返回未定义的异常 .

但是,我完全理解如何构建表达式树 . 这是我的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;
        }
    }
}

这是我的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.Expression1 [System.Func2 [Neo4jClient.Cypher.ICypherResultItem,Data.Model.Profile]]'的ParameterExpression不能用于'Neo4jClient.Cypher.ICypherResultItem'类型的委托参数

如何根据我的输入谓词构建一个带有动态参数名称的表达式,该谓词适合像 entity => entity.As<TEntity> 这样的Return调用?

1 回答

  • 1

    经过一段时间的修补,我想出了一个通用的存储库: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继承,以引用该实体的节点上的标签 . 通过使存储库具有通用性,我们可以轻松地为使用标准CRUD操作从Neo4jRepository继承的任何东西站起来 .

相关问题