首页 > 解决方案 > 如何在单个元素的表达式中使用表达式

问题描述

如何在另一个表达式中使用一个表达式。对于我可以使用的集合blog.Posts.Select(postMapper.ProjectPost)。但是如何将它用于单个对象?我不想调用编译,我需要在 EF sql 翻译器中使用它。我尝试了一些技巧,new List<Blog>{post.Blog}.Select(blogMapper.ProjectBlog).First()但它不起作用。

    public class BloggingContext : DbContext
    {
        private readonly ILoggerFactory loggerFactory;

        public BloggingContext(ILoggerFactory loggerFactory)
        {
            this.loggerFactory = loggerFactory;
        }

        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }
        
        protected override void OnConfiguring(DbContextOptionsBuilder options)
            => options.UseLoggerFactory(loggerFactory).UseSqlServer(@"Server=(LocalDB)\MSSqlLocalDb;Database=EFExpressionMapper;Trusted_Connection=True");
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public List<Post> Posts { get; } = new List<Post>();
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }

    class Program
    {
        static async Task Main(string[] args)
        {
            IServiceCollection serviceCollection = new ServiceCollection();
            serviceCollection.AddLogging(builder => builder
                .AddConsole()
                .AddFilter(level => level >= LogLevel.Information)
            );
            var loggerFactory = serviceCollection.BuildServiceProvider().GetService<ILoggerFactory>();

            await using var dbContext = new BloggingContext(loggerFactory);
            
            dbContext.Add(new Blog
            {
                Url = "http://blogs.msdn.com/sample-blog",
                Posts =
                {
                    new Post {Title = "Post 1", Content = "Post 1 content"},
                    new Post {Title = "Post 2", Content = "Post 2 content"},
                    new Post {Title = "Post 3", Content = "Post 3 content"},
                }
            });
            await dbContext.SaveChangesAsync();

            var postMapper = new PostMapper(new BlogMapper());

            var posts = await dbContext.Posts.Select(postMapper.ProjectPost).ToArrayAsync();

            foreach (var post in posts)
            {
                Console.WriteLine($"{post.Title} {post.Blog.Url}");
            }
        }
    }

    public class PostMapper
    {
        public Expression<Func<Post, PostDto>> ProjectPost { get; }

        public PostMapper(BlogMapper blogMapper)
        {
//TODO USE blogMapper.ProjectBlogList WITHOUT COMPILE
            ProjectPost = post => new PostDto(post.PostId, post.Title, post.Content, blogMapper.ProjectBlogList.Compile()(post.Blog));
        }
    }

    public class BlogMapper
    {
        public Expression<Func<Blog, BlogListDto>> ProjectBlogList { get; } = blog => new BlogListDto(blog.BlogId, blog.Url);
    }

    public class BlogListDto
    {
        public int BlogId { get; }
        public string Url { get; }
        
        public BlogListDto(int blogId, string url)
        {
            BlogId = blogId;
            Url = url;
        }
    }

    public class PostDto
    {
        public int PostId { get; }
        public string Title { get; }
        public string Content { get; }
        
        public BlogListDto Blog { get; }

        public PostDto(int postId, string title, string content, BlogListDto blog)
        {
            PostId = postId;
            Title = title;
            Content = content;
            Blog = blog;
        }
    }

查看 PostMapper 构造函数。我在那里使用了编译方法。但是对EF不好

标签: c#linqentity-framework-core

解决方案


实际上LINQKit可能会更正您的查询并使 EF 在使用Compile. 只需添加AsExpandable()到您的查询。

但我建议不要为每个 DTO 创建映射类,而是将它们收集在逻辑一个中:

public static class DtoMapper
{
    [Expandable(nameof(AsDtoPost))]
    public static PostDto AsDto(this Post post)
        => throw new NotImplementedException();

    [Expandable(nameof(AsDtoBlogList))]
    public static BlogListDto AsDto(this Blog blog)
        => throw new NotImplementedException();

    static Expression<Func<Post, PostDto>> AsDtoPost()
        => post => new PostDto(post.PostId, post.Title, post.Content, post.Blog.AsDto()));

    static Expression<Func<Blog, BlogListDto>> AsDtoBlogList()
        => blog => new BlogListDto(blog.BlogId, blog.Url);
}

所以你的样本可以重写

var posts = await dbContext.Posts
    .AsExpandable()
    .Select(p => p.AsDto()).ToArrayAsync();

类似但之前已经创建了答案,其中涵盖了执行相同 lambda 表达式扩展的其他库。https://stackoverflow.com/a/66386142/10646316 这个答案侧重于 LINQKit 实现。


推荐阅读