How can I use an Expression in another Expression. For a set I can use blog.Posts.Select(postMapper.ProjectPost)
. But How can I use it for a single object? I don't want to call compile, I need to use that in EF sql translator. I try some hacks like new List<Blog>{post.Blog}.Select(blogMapper.ProjectBlog).First()
but it's not working.
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;
}
}
Look into PostMapper constructor. I'm used a Compile method there. But it's not good for EF