Skip to content

Instantly share code, notes, and snippets.

@Zerg903
Created February 8, 2023 13:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zerg903/007967724a9d37f19856b083a1b6bf6e to your computer and use it in GitHub Desktop.
Save Zerg903/007967724a9d37f19856b083a1b6bf6e to your computer and use it in GitHub Desktop.
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
// projection types
// ----------------------------------
public class Projection<TSource, TResult>
{
private readonly Lazy<Func<TSource, TResult>> _lazyDelegate;
public Projection(Expression<Func<TSource, TResult>> expression)
{
Expression = expression;
_lazyDelegate = new Lazy<Func<TSource, TResult>>(Expression.Compile, LazyThreadSafetyMode.PublicationOnly);
}
internal Expression<Func<TSource, TResult>> Expression { get; }
internal Func<TSource, TResult> Delegate => _lazyDelegate.Value;
public TResult Map(TSource source) => Delegate(source);
}
public static class ProjectionExtensions
{
public static IQueryable<TDestination> Projection<TSource, TDestination>(
this IQueryable<TSource> queryable, Projection<TSource, TDestination> projection)
{
return queryable.Select(projection.Expression);
}
public static IEnumerable<TDestination> Projection<TSource, TDestination>(
this IEnumerable<TSource> enumerable, Projection<TSource, TDestination> projection)
{
return enumerable.Select(projection.Delegate);
}
}
// example
// ----------------------------------
public record class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public record CategoryDto
{
public static Projection<Category, CategoryDto> Mapper = new(c => new CategoryDto
{
Number = c.Id + 1,
Title = c.Name.ToUpper()
});
public int Number { get; set; }
public string Title { get; set; }
}
class Program
{
static void Main(string[] args)
{
// single map
var category = new Category { Id = 1, Name = "category 1" };
var dto = CategoryDto.Mapper.Map(category);
Console.WriteLine(dto);
Console.WriteLine("--------------");
// enumerable map
var categoryes = new[]{
new Category{ Id = 1, Name = "category 1" },
new Category{ Id = 2, Name = "category 2" },
new Category{ Id = 3, Name = "category 3" }
};
var dtos1 = categoryes
.Projection(CategoryDto.Mapper)
.ToList();
dtos1.ForEach(Console.WriteLine);
Console.WriteLine("--------------");
// queryable map
using var db = new AppContext();
db.Database.EnsureCreated();
var dtos2 = db.Categories
.Projection(CategoryDto.Mapper)
.ToList();
dtos2.ForEach(Console.WriteLine);
Console.WriteLine("--------------");
}
}
public class AppContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public string DbPath { get; }
public AppContext()
{
var folder = Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);
DbPath = System.IO.Path.Join(path, "blogging.db");
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={DbPath}");
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Category>().HasData(
new Category { Id = 1, Name = "category 1" },
new Category { Id = 2, Name = "category 2" },
new Category { Id = 3, Name = "category 3" }
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment