-
-
Save Zerg903/007967724a9d37f19856b083a1b6bf6e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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