Entity Framework IGrouping error when selecting data - c#

I have this model
public class Project
{
[Required]
public Guid Id { get; set; }
[MaxLength(512)]
[Required]
public string ProjectName { get; set; }
public Category Category { get; set; }
[ForeignKey("Category")]
public Guid? CategoryId { get; set; }
}
public class Category
{
[Required]
public Guid Id { get; set; }
public string CategoryName { get; set; }
public ICollection<Project> Projects { get; set; }
}
and this is view model:
public class TipoProyectoViewModel
{
public string CategoryName { get; set; }
public IEnumerable<Project> MProject { get; set; }
}
When I am writing my code to get a list of projects group by category
Category = House
{
{ Id = 1, ProjectName = "my project" }
{ Id = 2, ProjectName = "my project2" }
}
Category = Cars
{
{ Id = 3, ProjectName = "my project3" }
{ Id = 5, ProjectName = "my project5" }
}
This is what I have in my controller:
var categories = _dbContext.Category
.Include(c => c.Projects)
.GroupBy(e => e.CategoryName)
.Select(e => new TipoProyectoViewModel()
{
CategoryName = e.Key,
MProject = e.Key.Projects
});
My problem is that I am getting an error in my select when I write
MProject = e.Key.Projects
String does not contain a definition for Projects and no extension method "projects"accepting a first argument of type string could be found. Are you missing a reference?
what am I doing wrong?

You're accessing e.Key, which is a string key of a grouping. Apparently, it doesn't have a property or a method .Projects.
You can collect the Projects from your grouped items like this
MProject = e.SelectMany(cat => cat.Projects);

Related

EF Core 5 single property projection

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).

LINQ statement issue for dynamic view component menu

I have three level (Category- Subcategory - Nestedcategory) dropdown navigation menu on my website for which data must come dynamically from database. My main problem in generation of InvokeAsync() method to make it work. I can write two levels which work fine as I checked, but confused in defining Nestedcategories - need to get it from subcategories which derived from categories.
Here is my Controller
public class MenuViewComponent: ViewComponent
{
private readonly SamirDbContext _samirDbContext;
public MenuViewComponent(SamirDbContext samirDbContext)
{
_samirDbContext = samirDbContext;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var menu = await _samirDbContext.Categories.Include(x => x.Subcategories).ThenInclude(y => y.NestedCategories).
Select(x => new MenusModel()
{
Category = x,
Id = x.Id,
Subcategories = x.Subcategories,
**NestedCategories = ...**
}).ToListAsync();
return View(menu);
}
}
Here are models:
public class Category
{
public Category()
{
Subcategories = new HashSet<Subcategory>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Subcategory> Subcategories { get; set; }
}
public class Subcategory
{
public Subcategory()
{
Posts = new HashSet<Post>();
NestedCategories = new HashSet<NestedCategory>();
}
public int Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
public int CategoryId { get; set; }
public ICollection<Post> Posts { get; set; }
public ICollection<NestedCategory> NestedCategories { get; set; }
}
public class NestedCategory
{
public NestedCategory()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Post> Posts { get; set; }
public Subcategory Subcategory { get; set; }
public int SubcategoryId { get; set; }
}
Menu ViewModel
public class MenusModel
{
public int Id { get; set; }
public Category Category { get; set; }
public IEnumerable<Category> Categories { get; set; }
public IEnumerable<Subcategory> Subcategories { get; set; }
public Subcategory Subcategory { get; set; }
public IEnumerable<NestedCategory> NestedCategories { get; set; }
public NestedCategory NestedCategory { get; set; }
}
Please, help in completion InvokeAsyinc() method in order to get work for 3 level menu.
You can use SelectMany() method, change the linq like below:
var menu = await _samirDbContext.Categories
.Select(x => new MenusModel()
{
Category = x,
Id = x.Id,
Subcategories = x.Subcategories,
NestedCategories = x.Subcategories.SelectMany(s => s.NestedCategories).ToList()
}).ToListAsync();
Looking at your models for Category, Subcategory, and NestedCategory, I'm asking myself why you actually could need to have a separate output property (**NestedCategories = ...**) in your final Select statement.
Let's think this way if the NestedCategory is defined inside the Subcategory collection, then every Subcategory element should have its own list of NestedCategory-ies, which will be available when you will check some Subcategory from the dropdown.
So, my advice here is to leave the result as follows:
var menu = await _samirDbContext.Categories
.Include(x => x.Subcategories)
.ThenInclude(y => y.NestedCategories)
.Select(x => new MenusModel()
{
Category = x,
Id = x.Id,
Subcategories = x.Subcategories
.Select(sb => new SubcategoryDTO
{
sb.Id,
sb.Name,
...
NestedCategories = sb.NestedCategories
.Select(nst => new NestedCategoriesDTO
{
nst.Id,
nst.Name,
...
})
}),
}).ToListAsync();
Then you can use the above model in your UI.
Hope this will help ))

AutoMapper MapExpression for sub-entity fails

I'm mapping select expression (projection) of Linq query. This is done to decouple logic layer from data access layer and logic layer should use only DTOs.
Expression<Func<CountyInfoDto, CountyInfoDto>> selector = c =>
new CountyInfoDto
{
Id = c.Id,
Citizens = c.Citizens.Select(p => new CitizenDto
{
}).ToList()
};
var resEx = mapper.MapExpression<Expression<Func<CountyInfo, CountyInfoDto>>>(selector);
This mapping fails with error Expression of type 'DTOs.CitizenDto' cannot be used for return type 'Entities.Citizen' however in CountyInfoDto property Citizens has type CitizenDto. Please note all mapping profiles are valid and simple objects can be mapped properly.
If I do like this, all works:
Expression<Func<CountyInfoDto, CountyInfoDto>> selector = c =>
new CountyInfoDto
{
Id = c.Id
};
var resEx = mapper.MapExpression<Expression<Func<CountyInfo, CountyInfoDto>>>(selector);
or this also works:
Expression<Func<CountyInfoDto, CountyInfoDto>> selector = c =>
new CountyInfoDto
{
Id = c.Id,
Citizens = new List<CitizenDto>
{
new CitizenDto
{
Id = c.Citizens.First().Id
}
}
};
var resEx = mapper.MapExpression<Expression<Func<CountyInfo, CountyInfoDto>>>(selector);
is there any possibility to avoid this error?
Classes:
public class CountyInfo
{
public CountyInfo()
{
Citizens = new HashSet<Citizen>();
}
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<Citizen> Citizens { get; set; }
}
public class Citizen
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ZipCode { get; set; }
}
public class CountyInfoDto
{
public CountyInfoDto()
{
Citizens = new List<CitizenDto>();
}
public Guid Id { get; set; }
public string Name { get; set; }
public List<CitizenDto> Citizens { get; set; }
}
public class CitizenDto
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string ZipCode { get; set; }
}
Mappings:
CreateMap<CountyInfo, CountyInfoDto>().ReverseMap();
CreateMap<Citizen, CitizenDto>().ReverseMap();
I'm using AutoMapper.Extensions.ExpressionMapping, after update to latest version error is: No coercion operator is defined between types 'Entities.CountyInfo' and 'DTOs.CountyInfoDto'.

Grouping in linq in EF Core

I have the following data:
ImagesTable:
Id Name CategoryId
1 image1 1
2 image2 1
CategoryTable:
Id CategoryName Description
1 Team Building TB 2020
I am writing a query in linq to get all the category of images and the associated images within each category.
var query = from p in _context.ImagesCategory
join s in _context.ImagesGallery on p.Id equals s.CategoryId into groupcat
from s in groupcat
select new CategoryGalleryDto { Id = p.Id, CategoryName = p.ImageCategory, CategoryDescription = p.ImageDescription, ImagesList = new List<ImageGalleryDto> { new ImageGalleryDto { ImageName = s.ImageName } } };
var grouping = query.ToLookup(e => e.Id).ToList();
My query is returning me three categories despite I have only one category. Please help
My DTOS
public class CategoryGalleryDto
{
public int Id { get; set; }
public string CategoryName { get; set; }
public string CategoryDescription { get; set; }
public DateTime UploadedDate { get; set; }
public List<ImageGalleryDto> ImagesList { get; set; }
}
public class ImageGalleryDto
{
public int Id { get; set; }
public string ImageName { get; set; }
public int CategoryId { get; set; }
}

C# EF add data to model

I have those models
class Artist
{
public int Id { get; set; }
[StringLength(500)]
public string Name { get; set; }
[StringLength(50)]
public string LastName { get; set; }
public virtual ICollection<SimilarArtist> SimilarArtists { get; set; }
}
class SimilarArtist
{
public int Id { get; set; }
public int ArtistId { get; set; }
[ForeignKey("ArtistId")]
public Artist Artist { get; set; }
public int Similar_Artist_Id { get; set; }
}
So each artist have links to other 5 from the same table. When the migration generate database it made that stracture.
SELECT [Id]
,[Name]
,[LastName]
FROM [dbo].[Artists]
SELECT [Id]
,[ArtistId]
,[Similar_Artist_Id]
FROM [dbo].[SimilarArtists]
So when I do select the model it return this
var similar = _db.Artists.FirstOrDefault(x => x.Name == id).SimilarArtists.FirstOrDefault();
//similar.ArtistId
//similar.Id
//similar.Similar_Artist_Id
//similar.Artist //the object which return main artist
The question is how I can get in "var similar" not just Similar_Artist_Id but also name and lastname in the same request (without making requests by Similar_Artist_Id)
var similarId = model.SimilarArtists.FirstOrDefault().Id;
var artiest = _db.Artists.Where(x.Id = similarId);
or just:
similar.Artist.Name
Or if you want to be able to have strongly-type property such as similar.ArtistName, create a [NotMapped] getter property.
class SimilarArtist
{
public int Id { get; set; }
public int ArtistId { get; set; }
[ForeignKey("ArtistId")]
public Artist Artist { get; set; }
public int Similar_Artist_Id { get; set; }
[NotMapped]
public string ArtistName
{
get
{
return this.Artist.Name;
}
}
}
You can do
var similar = _db.Artists.Where(x => x.Name == id)
.Select(a => a.SimilarArtists.FirstOrDefault())
.FirstOrDefault();
This gives you the first SimilarArtists (with all of its properties) of the first Artists matching the predicate x.Name == id.

Categories

Resources