2

I want to reduce duplicated code. In order to achieve that I want to reference the projections of my Entities.

Entities

public class Category
{
    public string Id { get; set; }
    public string CategoryName { get; set; }
    public static Expression<Func<Category, Category>> Proj() => c => new Category
    {
        CategoryName = c.CategoryName
    };
}
public class Image
{
    public string Id { get; set; }
    public string Url { get; set; }
    public static Expression<Func<Image, Image>> Proj() => i => new Image
    {
        Url = i.Url
    };
}
public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
    public ICollection<Image> Images { get; set; }
    public Category Category { get; set; }
}

Projection Query

var categoryProjection = Category.Proj().Compile();
var products = _ctx.Products.Select(p => new Product
{
    Id = p.Id,
    Name = p.Name,
    Images = p.Images.AsQueryable().Select(Image.Proj()).ToHashSet(),
    Category = categoryProjection.Invoke(p.Category)
});

When I execute the projection then it will work correctly for Product and Images. But for Category the genereted SQL will contain all Columns (Id and CategoryName).

After_8
  • 189
  • 1
  • 4
  • 16
  • It is LINQKit usage? – Svyatoslav Danyliv May 06 '21 at 18:02
  • If you want to hide `Id` you could also make the property `internal`. A better alternative is to map to classes without `Id` using AutoMapper. Using these projection functions is quite a hassle IMO. – Gert Arnold May 06 '21 at 18:35
  • Automapper to get single property? I have missed something in this world. – Svyatoslav Danyliv May 06 '21 at 18:39
  • I'm not really clear on what you are trying to achieve, or how this will 'remove duplicated code', but this will probably break EF in ways you haven't yet found (broken or inefficient queries for example). If you want to project a query, then just use Select (and include) correctly, and as @GertArnold says, AutoMapper would probably help too. This will also help whoever looks at your code in 6 months time, and wonders what on earth you were trying to do (this person might be you!). – Neil May 06 '21 at 19:04
  • @Svyatoslav It's probably a simplified example, but nevertheless, see `Product.Images`, `Product.Category`. `products` could be produced by one short `ProjectTo` call. – Gert Arnold May 06 '21 at 19:10
  • Actually i don't want to depend on AutoMapper i already tried it and had some intransparent results. Also AutoMapper isn't known for performance. On @Neil's comment: I want to reduce duplicated code, because I find myself rewriting the same projections in many of my controller actions. With such a solution I'm able to reference just one projection. Also i get the possiblity to add parameters to my projections. – After_8 May 06 '21 at 19:54
  • 1
    Where do you get that Automapper isn't known for performance? In some cases it's faster than handcrafted a=b ! And you are suggesting that one of the most used nuget packages is 'unreliable' ? – Neil May 06 '21 at 19:56
  • 1
    Maybe this my answer will be helpful for you https://stackoverflow.com/a/66386142/10646316 – Svyatoslav Danyliv May 06 '21 at 20:24
  • without some 3rd party expression library you have to basically write most of the things they do. `AsQueryable()` trick works (in most of the cases) for enumerables, but the way you overcome the compile time problem with compiling `categoryProjection` **delegate** doesn't. Because now you have delegate call inside query expression tree, which is the same as calling unknown code. In fact you are lucky that it even works, since client evaluation is still supported in the *final* projection. Add some operator after `Select` and boom - exception. You really need something which injects the – Ivan Stoev May 07 '21 at 06:50
  • ... expression instead of compiling it. The answer linked by Svyatoslav recaps some of the libraries that can be used. I have similar answers too, with `DelegateDecompiler` plugged into EF Core query pipeline being my personal favorite - see https://stackoverflow.com/questions/62115690/ef-core-queries-all-columns-in-sql-when-mapping-to-object-in-select/62138200#62138200 – Ivan Stoev May 07 '21 at 06:55

0 Answers0