This question already has an answer here:
EFCore Linq ThenInclude Two Foreign Keys To Same Table
(1 answer)
Closed 5 years ago.
I have 4 classes
public class Customer
{
public string CustomerName { get; set; }
public ICollection<Order> Orders{ get; set; }
}
public class Order
{
public string OrderNumber { get; set; }
public ICollection<OrderLine> OrderLines { get; set; }
public OrderType Type { get; set; }
}
public class OrderLine
{
public string StockItem { get; set; }
}
public class OrderType
{
public string Type { get; set; }
}
Using entity frameworks i want to pull out all the information starting at Customer level. I can get most information out but i am stuck getting OrderType to show against the order.
This is what i have so far.
var customerOrderDetails = _myOrderRepository
.GetAll()
.Include(o => o.Orders)
.ThenInclude(l => l.OrderLines)
I've tried added select after the o.Orders but that doesn't seem to work.
You can do something like this:
var customerOrderDetails = _myOrderRepository
.GetAll()
.Include(o => o.Orders)
.Include(o => o.Orders.Select(l => l.OrderLines))
.Include(o => o.Orders.Select(t => t.Type))
var customerOrderDetails = _myOrderRepository
.GetAll()
.Include(o => o.Orders)
.ThenInclude(l => l.OrderLines)
.Include(o => o.Orders).ThenInclude(t => t.Type)
Adding another Include after the OrderLines and then selecting the type seemed to work.
Related
i am using entity framework core 7 in my .net 7 project. Here is my update method
public async Task UpdateAsync(int id, SiteDto dto)
{
Site site = await _context.Sites.Where(x => x.Id == id)
.Include(x => x.Network)
.Include (x => x.Centre)
.Include(x => x.SiteDayParts).ThenInclude(x => x.SiteFrames)
.Include(x => x.Resolution)
.SingleOrDefaultAsync();
_mapper.Map(dto, site);
await _context.SaveChangesAsync();
}
SiteDto object, context has the same fields
public class SiteDto
{
public string Name { get; set; }
public string Address { get; set; }
public MediaFormat Formats { get; set; }
public int ResolutionId { get; set; }
public List<SiteDayPartDto> SiteDayParts { get; set; }
}
public class SiteDayPartDto
{
public int Id { get; set; }
public TimeSpan StartTime { get; set; }
public TimeSpan EndTime { get; set; }
public int Duration { get; set; }
public List<SiteFrameDto> SiteFrames { get; set; }
}
public class SiteFrameDto
{
public int Id { get; set; }
public int FrameId { get; set; }
}
Mapping config
CreateMap<SiteDto, Site>();
CreateMap<SiteDayPartDto, SiteDayPart>()
.ForMember(dest => dest.AdLength, opt => opt.MapFrom(x => x.Duration));
CreateMap<SiteFrameDto, SiteFrame>()
When I trying to save changes, I get an error "The instance of entity type 'SiteFrame' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached."
Database - Postgresql.
Context registered as scoped, mapper, as I know, don`t create new object.
It is interesting that, if I use entity framework 6, it works well.
I have solved the problem like this. First, install Automapper.Collections and add:
services.AddAutoMapper((serviceProvider, automapper) =>
{
automapper.AddCollectionMappers();
}, Assembly.GetExecutingAssembly());
Then if you have nested list with a nested list, as in my example, add this mapping:
CreateMap<SiteDayPartDto, SiteDayPart>()
.EqualityComparison((odto, o) => odto.Id == o.Id)
CreateMap<SiteFrameDto, SiteFrame>()
.EqualityComparison((odto, o) => odto.Id == o.Id)
EqualityComparison will check if entity contains dto or not (it may be useful https://github.com/AutoMapper/Automapper.Collection). Good luck!
I'm using EF Core for my project. And I have a problem with nested query in EF Core.
I have 2 classes:
public class PermissionGroupDefinitionEntity : IEntity
{
public string Name { get; set; }
public string NormalizedName { get; set; }
public string DisplayName { get; set; }
public virtual ICollection<PermissionDefinitionEntity> PermissionDefinitions { get; set; }
}
public class PermissionDefinitionEntity : IEntity
{
public string Name { get; set; }
public string NormalizedName { get; set; }
public string DisplayName { get; set; }
public bool IsEnabled { get; set; }
public virtual string GroupName { get; set; }
public virtual PermissionGroupDefinitionEntity Group { get; set; }
public virtual ICollection<PermissionDefinitionEntity> Children { get; set; }
}
and this is the ApplicationDbContext:
builder.Entity<PermissionDefinitionEntity>().HasOne(r => r.Group).WithMany(r => r.PermissionDefinitions).OnDelete(DeleteBehavior.Cascade);
builder.Entity<PermissionDefinitionEntity>().HasOne(r => r.Parent).WithMany(r => r.Children).OnDelete(DeleteBehavior.Cascade);
I want query all PermissionGroupDefinitionEntity included PermissionDefinitionEntity and self referencing of PermissionDefinitionEntity.
Can I do that with EF Core?
You need to recursively load PermissionDefinitions that placed in the PermissionGroupDefinitionEntity.
First, you should load all PermissionGroupDefinitionEntities including its children using the following query :
var query = _dbContext.PermissionGroupDefinitionEntity
.AsNoTracking()
.Include(p => p.PermissionDefinitions )
.ThenInclude(p => p.Children)
.ToListAsync();
Since every PermissionGroupDefinitionEntity has a list of PermissionDefinition you need a nested loops like this code :
foreach (var PermissionGroupDefinitionEntity in PermissionGroupDefinitionEntities)
{
foreach (var PermissionDefinitions in PermissionDefinitions)
{
}
}
Then in the inner loop you should call your recursive function.
See following link (sample for get all children recursively in Entity Framework Core)
https://patrickdesjardins.com/blog/how-to-load-hierarchical-structure-with-recursive-with-entity-framework-5
This way has terrible performance and I don't recommend that.
In this case it's seems you must write a stored procedure in SQL for better performance.
You can use .ThenInclude(i => ...) like so
var query = _context.PermissionGroupDefinitionEntity
.AsNoTracking()
.Include(i => i.PermissionDefinitions)
.ThenInclude(i => i.Group)
.AsQueryable();
Edit:
var query = _context.PermissionGroupDefinitionEntity
.AsNoTracking()
.Include(i => i.PermissionDefinitions)
.ThenInclude(i => i.Children)
.AsQueryable();
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I have a small Web Api method. How can I map properties in GetBooks method with Automapper? I have tried the solution here .
I have tried
return from c in db.Books select Mapper.Map<BookDTO>(c);
But it didnt work.
Here is my complete code;
//GET: api/Books
public IQueryable<BookDTO> GetBooks()
{
var books = from b in db.Books
select new BookDTO()
{
Id = b.Id,
Title = b.Title,
AuthorName = b.Author.Name
};
return books;
}
Book.cs
public class Book
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
public int Year { get; set; }
public decimal Price { get; set; }
public string Genre { get; set; }
// Foreign Key
public int AuthorId { get; set; }
// Navigation property
public Author Author { get; set; }
}
BookDTO.cs
public class BookDTO
{
public int Id { get; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
}
thanks in advance.
Config Map between Book and BookDTO in Global.asax.cs or Startup.cs file:
Mapper.CreateMap<Book, BookDTO>();
Map db.Books in GetBooks() method:
using AutoMapper.QueryableExtensions;
...
public IQueryable<BookDTO> GetBooks()
{
return db.Books.ProjectTo<BookDTO>();
}
You need projection. See Automapper - Projection
Mapper.CreateMap<Book, BookDTO>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Title, opt => opt.MapFrom(src => src.Title))
.ForMember(dest => dest.AuthorName, opt => opt.MapFrom(src => src.Author.Name));
IEnumerable<Book> books = db.Books.Cast<Book>();
// assuming Books implements IEnumerable
BookDTO result = Mapper.Map<Book, BookDTO> books;
I'm not sure if you need the projection lines for properties Id and Title. Read the documentation of AutoMapper to see what defaults it uses.
I have read a lot of the questions about that same error but none since to match my exact problem. I'm trying to access the property of an object, itself part of a root object, using Fluent NHibernate. Some answers say I need to use projections, others that I need to use join, and I think it should work through lazy loading.
Here are my two classes along with the Fluent mappings:
Artist class
public class Artist
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Album> Albums { get; set; }
public virtual string MusicBrainzId { get; set; }
public virtual string TheAudioDbId { get; set; }
public Artist() { }
}
public class ArtistMap : ClassMap<Artist>
{
public ArtistMap()
{
LazyLoad();
Id(a => a.Id);
Map(a => a.Name).Index("Name");
HasMany(a => a.Albums)
.Cascade.All();
Map(a => a.MusicBrainzId);
Map(a => a.TheAudioDbId);
}
}
Album class
public class Album
{
public virtual int Id { get; set; }
public virtual Artist Artist { get; set; }
public virtual string Name { get; set; }
public virtual IList<Track> Tracks { get; set; }
public virtual DateTime ReleaseDate { get; set; }
public virtual string TheAudioDbId { get; set; }
public virtual string MusicBrainzId { get; set; }
public Album() { }
}
public class AlbumMap : ClassMap<Album>
{
public AlbumMap()
{
LazyLoad();
Id(a => a.Id);
References(a => a.Artist)
.Cascade.All();
Map(a => a.Name).Index("Name");
HasMany(a => a.Tracks)
.Cascade.All();
Map(a => a.ReleaseDate);
Map(a => a.TheAudioDbId);
Map(a => a.MusicBrainzId);
}
}
And the error happens when this code is interpreted:
var riAlbum = session.QueryOver<Album>()
.Where(x => x.Name == albumName && x.Artist.Name == artist)
.List().FirstOrDefault();
The error happens when Fluent NHibernate tries to resolve the x.Artist.Name value:
{"could not resolve property: Artist.Name of: Album"}
What would be the correct way of doing this?
You have to think of your QueryOver query as (nearly) directly translating into SQL. With this in mind, imagine this SQL query:
select
Album.*
from
Album
where
Album.Name = 'SomeAlbumName' and
Album.Artist.Name = 'SomeArtistName'
This won't work because you can't access a related table's properties like that in a SQL statement. You need to create a join from Album to Artist and then use a Where clause:
var riAlbum =
session.QueryOver<Album>()
.Where(al => al.Name == albumName)
.JoinQueryOver(al => al.Artist)
.Where(ar => ar.Name == artistName)
.List()
.FirstOrDefault();
Also, since you're using FirstOrDefault, you may want to consider moving that logic to the database end. Currently, you're pulling back every record matching your criteria and then taking the first one. You could use .Take to limit the query to 1 result:
var riAlbum =
session.QueryOver<Album>()
.Where(al => al.Name == albumName)
.JoinQueryOver(al => al.Artist)
.Where(ar => ar.Name == artistName)
.Take(1)
.SingleOrDefault<Album>();
Another explanation is that you are missing your mapping of this property or field in a NHibernateClassMapping definition. I came here about why I was getting this error based on the following scenario.
var query = scheduleRepository.CurrentSession().Query<Schedule>()
.Where(x => x.ScheduleInfo.StartDate.Date < dateOfRun.Date);
This was giving me a Could Not Resolve Property error for StartDate. This was a head scratcher, since I use this syntax all the time.
My mapping file was the following:
public class ScheduleInfoMapping : NHibernateClassMapping<ScheduleInfo>
{
public ScheduleInfoMapping()
{
DiscriminateSubClassesOnColumn("Type");
Map(x => x.Detail).MapAsLongText();
}
}
which was missing the StartDate. Changed to:
public class ScheduleInfoMapping : NHibernateClassMapping<ScheduleInfo>
{
public ScheduleInfoMapping()
{
DiscriminateSubClassesOnColumn("Type");
Map(x => x.Detail).MapAsLongText();
Map(x => x.StartDate);
}
}
Which resolved the error.
I have two entities like:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Lastname { get; set; }
public virtual ICollection<EmployeeEducation> EducationList { get; set; }
}
and
public class EmployeeEducation
{
public int Id { get; set; }
public int EmployeeId { get; set; }
public int Type { get; set; }
[ForeignKey("EmployeeId")]
public virtual Employee Employee { get; set; }
}
My question is, how can I get a specific employee and this employee's education list ordered by Type property?
I have tried:
Employee employee = _work.EmployeeRepository.GetSet()
.SelectMany(e => e.EducationList, (e,d) => new { e, d })
.OrderBy(x => x.d.Type)
.Select(x => x.e)
.FirstOrDefault(e => e.Id == id);
But it does't seem to be sorting. What is the correct way to do this?
Thanks for everyone...
You do SelectMany(), but never use the produced EducationList part, becuase you do .Select(x => x.e). But couldn't life be simpler? After all, you only get 1 employee, why not sort its EducationList as soon as you need it, after having Included it, if necessary:
Employee employee = _work.EmployeeRepository.GetSet().Include("EducationList")
.FirstOrDefault(e => e.Id == id);
Depending if u are using POCO or not u should either use CreateSourceQuery() or Query()
In the case of POCO something like:
Employee employee = _work.EmployeeRepository.GetSet()
.SelectMany(e => e.EducationList, (e,d) => new { e, d })
.Query()
.OrderBy(x => x.d.Type)
.Select(x => x.e)
.FirstOrDefault(e => e.Id == id);