I'm trying to get related tables Adress and PlzOrt into an object User.
The relationships are as follows:
User 1:1 Adress
Adress n:1 PlzOrt
Entities Scaffolded from the DB
public partial class User
{
//No hash sets for Adress Scaffolded (probably unnecessary since the is
//only ever one address per user
public int IdUser { get; set; }
public int? FidAdresse { get; set; }
public string Email { get; set; }
public string Vorname { get; set; }
public string Nachmname { get; set; }
[ForeignKey("FidAdresse")]
[InverseProperty("User")]
public virtual Adresse FidAdresseNavigation { get; set; }
}
public partial class Adresse
{
public Adresse()
{
User = new HashSet<User>();
}
public int IdAdresse { get; set; }
public int FidPlzOrt { get; set; }
public string Strasse { get; set; }
public string Hausnr { get; set; }
[ForeignKey("FidPlzOrt")]
[InverseProperty("Adresse")]
public virtual PlzOrt FidPlzOrtNavigation { get; set; }
[InverseProperty("FidAdresseNavigation")]
public virtual ICollection<User> User { get; set; }
}
public partial class PlzOrt
{
public PlzOrt()
{
Adresse = new HashSet<Adresse>();
}
public int IdPlzOrt { get; set; }
public string Plz { get; set; }
public string Ort { get; set; }
[InverseProperty("FidPlzOrtNavigation")]
public virtual ICollection<Adresse> Adresse { get; set; }
}
Here´s the linq that does not work.
return _context.User
.Include(u => u.FidPermissionNavigation)
.Include(c => c.FidAdresseNavigation)
.ThenInclude(c => c.FidPlzOrtNavigation)
.FirstOrDefault(c => c.IdUser == id);
The linq work when I don´t include the "ThenInclude(c => c.FidPlzOrtNavigation)" statement, but I want this information in my object.
Here´s the C# that gives me the expected results:
public User GetUser(int id)
{
foreach (User user in _context.User)
{
if (user.IdUser == id)
{
foreach (Adresse adresse in _context.Adresse)
{
if (adresse.IdAdresse == user.FidAdresse)
{
user.FidAdresseNavigation = adresse;
foreach (PlzOrt plzOrt in _context.PlzOrt)
{
if (plzOrt.IdPlzOrt == adresse.FidPlzOrt)
{
user.FidAdresseNavigation.FidPlzOrtNavigation = plzOrt;
break;
}
}
break;
}
}
return user;
}
}
return null;
}
Translating this linq statement would be of great help. Thanks in advance.
Generated db_context code in case you are interested or this helps
modelBuilder.Entity<User>(entity =>
{
entity.HasKey(e => e.IdUser)
.HasName("PRIMARY");
entity.HasIndex(e => e.FidAdresse)
.HasName("fk_User_Adresse1_idx");
entity.HasOne(d => d.FidAdresseNavigation)
.WithMany(p => p.User)
.HasForeignKey(d => d.FidAdresse)
.HasConstraintName("fk_User_Adresse1");
});
modelBuilder.Entity<Adresse>(entity =>
{
entity.HasKey(e => e.IdAdresse)
.HasName("PRIMARY");
entity.HasIndex(e => e.FidPlzOrt)
.HasName("fk_Adresse_plz_ort1_idx");
entity.HasOne(d => d.FidPlzOrtNavigation)
.WithMany(p => p.Adresse)
.HasForeignKey(d => d.FidPlzOrt)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("fk_Adresse_plz_ort1");
});
modelBuilder.Entity<PlzOrt>(entity =>
{
entity.HasKey(e => e.IdPlzOrt)
.HasName("PRIMARY");
});
So you have an id, and you want the one and only user that has this id as primary key, together with his Address and his PlzOrt.
Whenever you query, use Select to fetch the data. Only use Include if you want to update the fetched data.
The reason for Select is that you have greater freedom of what you select. Besides you can limit the fetched data: if you query Schools with their Students, you know that every Student of School 10 will have a foreign key SchoolId equal to 10. So why fetch this foreign key for every of the Schools 1000 Students?
I'm not familiar with the possibilities of ef-core. Does it know that if you Select one of the virtual properties that a (group-)join is needed? In that case it is easier to use Select.
If you'll have to do your joins yourself:
var requestedUserWithAddresAndPlz = dbContext.Users.
// keep only the user with id
.Where(user => user.IdUser == id)
// every User has exactly one Address: use a normal join
.Join(dbContext.Addresses,
user => user.IdUser, // from every User take IdUser
address => addres.IdAddress, // from every Address take IdAddress
// result selector: take the user with its address to make one new
(user, address) => new
{
// Select only the User properties you plan to use
Id = user.IdUser,
Name = user.Name,
...
Address = new
{
// again: select only the properties you plan to use
// no need to select IdAddress, you know the value!
Street = address.Street,
HouseNumber = address.HouseNumber,
// to fetch the Plz: fetch the one-and-only PlzOrt with address.FidPlzOrt
Plz = dbContext.PlzOrts
.Where(plzOrt => plzOrt.PlzOrdIt == address.FidPlzOrt)
.FirstOrDefault(),
})
.FirstOrDefault();
Note: I used anonymous types to have greater freedom in selecting only the properties I actually plan to use. I can also give my properties the names that I want.
Disadvantage: you can't use anonymous types as return values. If you really need this outside your function, use create a class that contains your data, and use new SomeClass(). Advantage: if your database changes, SomeClass doesn't have to change, and thus your callers won't notice the change in your database.
Related
This is not a duplicate question as I have looked up many questions including this, which is the closest to what I want but didn't solve the challenge.
I have my table models relation set up this way:
public class User
{
public long UserId { get; set; }
public string Name { get; set; }
public IList<Transaction> Transactions { get; set; }
}
public class Transaction
{
public long TransactionId { get; set; }
public User User { get; set; }
public User Patient { get; set; }
}
fluent api setup for the entity
//some other modelbuilder stuff
modelBuilder.Entity<User>(entity =>
{
entity.HasMany(e => e.Transactions).WithOne(e => e.User);
//wanted to add another entity.HasMany(e => e.User).WithOne(e => e.Patient) but efcore didn't allow me.
});
This generates a Transaction table with UserUserId and PatientUserId and takes the right values on save.
But when I do a get with a user Id
User user = dbcontext.Set<User>().Include(t => t.Transactions).FirstOrDefault(u => u.UserId == userId);
user.Transactions have a list of transaction all with null Transaction.Patient
What exactly is going on here and how do I get past it?
Thanks.
You are nesting navigations. So, you have to use ThenInclude like this to add Patient which is a navigation property of Transaction.
User user = dbcontext.Set<User>().Include(t => t.Transactions).ThenInclude(p => p.Patient).FirstOrDefault(u => u.UserId == userId);
I am trying to learn asp.NetCore 2.2. I am trying to setup a simple one page site. I have run into a problem with Automapper where manual Mappinng using forMember() is working at a top level for CreateMap<Listing, ListingSearchResultsDto>().ForMember(ListingPhotosUrl) but not at a lower level. I have another mapping CreateMap<User, UserDetailsDto>() where user contains an object Mylistings of type Listing. Mylistings is correctly auto mapped to ListingSearchResultsDto but manual configuration CreateMap<Listing, ListingSearchResultsDto>().ForMember(ListingPhotosUrl) is not applied.
I Have tried CreateMap<User, UserDetailsDto>().Formember(dest.Mylistings.ListingPhotosUrl,src.Mylistings.Photos.Url) but it seems that is not possible.
I Also tried this-> But no luck
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDetailsDto>();
cfg.CreateMap<Listing, ListingSearchResultsDto>()
.ForMember(dest => dest.ListingPhotosUrl, opt =>
{
opt.MapFrom(src => src.Photos.FirstOrDefault(p => p.IsMain).Url);
});
});
var mapper = config.CreateMapper();
The Code:
AutoMappperProfiles
public AutoMapperProfiles()
{
CreateMap<Listing, ListingSearchResultsDto>()
.ForMember(dest => dest.ListingPhotosUrl, opt =>
{
opt.MapFrom(src => src.Photos.FirstOrDefault(p => p.IsMain).Url);
});
CreateMap<User, UserDetailsDto>();
CreateMap<ListingPhoto, ListingPhotosDetailedDto>();
}
User
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public ICollection<Listing> MyListings { get; set; }
}
UserDetailsDto
public class UserDetailsDto
{
public int Id { get; set; }
public string Username { get; set; }
public ICollection<ListingSearchResultsDto> MyListings { get; set;}
}
Listing
public int id { get; set; }
public string Category { get; set; }
public string Title { get; set; }
public ICollection<ListingPhoto> Photos { get; set; }
ListingSearchResultsDto
public class ListingSearchResultsDto
{
public int id { get; set; }
public string Title { get; set; }
public string ListingPhotosUrl { get; set; }
}
I am using CreateMap<Listing, ListingSearchResultsDto>().Formember(des,src) to manually map a destination property ListingPhotosUrl. I have another mapping CreateMap<User, UserDetailsDto>(). Inside User & UsedetailsDto classes i have a objects called MyListings of types ICollection<Listing> and ICollection<ListingSearchResultsDto> respectively. MyListings object is auto mapped correctly but ListingPhotosUrl manual mapping is not being applied. CreateMap<Listing,ListingSearchResultsDto>.Formember(des,src)) manual mapping is working at top level, but not at deeper level inside CreateMap<User, UserDetailsDto>(), is there anyway to fix this? thanks
FIXED - Automapper was working fine. Issue in Entity Framework DbContext. I did not include the photos as related data in the EF Core method for loading USER data GETUSER(). It was working with EF Core method for loading LISTING GetListing() because i had an include for photos Include(p => p.Photos).
After adding .ThenInclude(p => p.Photos) in GetUser(), the photos were returned with USER data and automapper successfully mapped User data and ListingPhotosUrl manual mapping was applied successfully.
Entity Framework Core DbContext:
public async Task<User> GetUser(int id)
{
var user = await _context.Users
.Include(a => a.Avatar)
.Include(l => l.MyListings)
.ThenInclude(p => p.Photos)
.FirstOrDefaultAsync(u => u.Id == id);
return user;
}
public async Task<Listing> GetListing(int id)
{
var listing = await _context.Listings
.Include(p => p.Photos)
.FirstOrDefaultAsync(l => l.id == id);
return listing;
}
I have a use-case with a deeply nested class hierarchy, for example like this:
public class Parent
{
public int Id { get; set; }
public List<ChildOne> Children { get; set; }
}
public class ChildOne
{
public int Id { get; set; }
public int ParentId { get; set; }
public List<ChildTwo> ChildrenTwo { get; set; }
}
public class ChildTwo
{
public int Id { get; set; }
public int Priority { get; set; }
public int ChildOneId { get; set; }
public List<ChildThree> ChildrenThree { get; set; }
}
public class ChildThree
{
public int Id { get; set; }
public int ChildTwoId { get; set; }
}
If I want to load all parent-objects and their related children levels, I'd do this:
var objects = context.Parent
.Include(parent => parent.Children)
.ThenInclude(childOne => childOne.ChildrenTwo)
.ThenInclude(childTwo => childTwo.ChildrenThree)
.ToList();
But what if I want my ChildrenTwo entities in the eager-loaded navigational property of ChildOne to be ordered by their Priority? I've done some research, and from the links below (and some others), it is apparently not directly possible in EF Core (yet):
https://github.com/aspnet/EntityFrameworkCore/issues/9445
https://github.com/aspnet/EntityFrameworkCore/issues/2919
https://github.com/aspnet/EntityFrameworkCore/issues/9067
So, how can you achieve the ordering of the ChildrenTwo above (by Priority) in a good/clean way that is fast? That probably means most of the work should happen on the DB server and not on the .NET client side. What's the best approach here?
Though it is very late to answer, but it may help the future readers:
I will explain the code:
var authorArticles = await _context.AuthorArticles
.Include(a => a.Author)
.ThenInclude(p => p.Person)
.ThenInclude(pq => pq.Qualifications)
.ThenInclude(q => q.QualificationSubject)
.Include(a => a.Author)
.ThenInclude(p => p.Person)
.ThenInclude(pp => pp.Professions)
.Include(a => a.Author)
.ThenInclude(p => p.Person)
.ThenInclude(pp => pp.Professions)
.ThenInclude(prof => prof.Profession)
.Where(aa => aa.ArticleId == articleId)
.Select(s => new AuthorArticle
{
Author = new Author
{
Affiliation = s.Author.Affiliation,
AvailableAsReviewer = s.Author.AvailableAsReviewer,
Person = new Person
{
Email = s.Author.Person.Email,
FirstName = s.Author.Person.FirstName,
LastName = s.Author.Person.LastName,
MiddleName = s.Author.Person.MiddleName,
Title = s.Author.Person.Title,
FullName = s.Author.Person.FullName,
UserId = s.Author.Person.UserId,
Professions = new Collection<PersonProfession>
{
new PersonProfession
{
// using sorting here!!
Organization = s.Author.Person.Professions
.OrderByDescending(pid => pid.ProfessionId)
.FirstOrDefault().Organization,
Profession = s.Author.Person.Professions
.OrderByDescending(pid => pid.ProfessionId)
.FirstOrDefault().Profession
}
},
Qualifications = new Collection<PersonQualification>
{
new PersonQualification
{
QualificationSubject = s.Author.Person.Qualifications
.OrderByDescending(q => q.QualificationLevelId)
.FirstOrDefault().QualificationSubject,
QualificationLevelId = s.Author.Person.Qualifications
.OrderByDescending(q => q.QualificationLevelId)
.FirstOrDefault().QualificationLevelId
}
}
}
},
IsCorresponding = s.IsCorresponding,
AuthorPosition = s.AuthorPosition
}).ToListAsync();
return authorArticles;
If you simply eager loaded the entities, then at the time of projection; which means when you are selecting the items from the query, you can recreate the object that has already been provided in slightly different way. In my case, I wanted only one profession of the person out of many and same goes for the qualification of the person.
Took help of select from Another SO great answer!
So I have 3 entity classes:
public partial class Event
{
public Event()
{
Recurrences = new HashSet<Recurrence>();
}
public int Id { get; set; }
public ICollection<Recurrence> Recurrences { get; set; }
}
public partial class Recurrence
{
public Recurrence()
{
AspNetUsers = new HashSet<AspNetUser>();
}
public int Id { get; set; }
public int EventId { get; set; }
public ICollection<AspNetUser> AspNetUsers { get; set; }
}
public partial class AspNetUser
{
public AspNetUser()
{
Recurrences = new HashSet<Recurrence>();
}
public string Id { get; set; }
public string UserName { get; set; }
public ICollection<Recurrence> Recurrences { get; set; }
}
I would like to get the event given the aspnetuser.id using line to entity. so far this is what I have but it's returning an error:
// GET: api/Events?userId={userId}
public IQueryable<Event> GetEvents(string userId)
{
return db.Events
.Include(e => e.Recurrences
.Select(u => u.AspNetUsers.Where(i => i.Id == userId)));
}
When I exclude the where clause it works fine. Please help.
Thanks in advance!
I don't think Include() means what you think it means. (https://msdn.microsoft.com/en-us/library/bb738708%28v=vs.110%29.aspx) What it does is tell the db set to be sure to bring in relationships for that object. By default (last I checked), the db context will auto pull in all relationships, so this isn't necessary. However, if you've turned off the lazy-loading (http://www.entityframeworktutorial.net/EntityFramework4.3/lazy-loading-with-dbcontext.aspx) then you'll need to .Include() all the relationships you want to have in the query.
This should solve your problem. I don't guarantee the SQL generated won't be silly, though.
If you have lazy-loading turned on:
db.Events.Include("Recurrences").Include("Recurrences.AspNetUsers")
.Where(e => e.Recurrences
.Any(r => r.AspNetUsers
.Any(u => u.Id ==userId)));
If you have lazy-loading turned off:
db.Events
.Where(e => e.Recurrences
.Any(r => r.AspNetUsers
.Any(u => u.Id ==userId)));
Also, if you have trouble seeing errors, you can .ToList() the query before returning so that it fails in your code and not deep inside the Web API stack. Personally, I like to do this so that I can try/catch the query and handle it properly.
I have two tables in my database. One is called Users, and the other is called Widgets. The Widgets table represents 3 entities in my code model. One of the entities, Widget, is a parent class for the other two entities, WidgetTypeA and WidgetTypeB. Both WidgetTypeA and WidgetTypeB have navigation properties to the User entity, which is persisted to the Users table in the database. I'm having trouble getting Code First to use the same foreign key for both the WidgetTypeA and WidgetTypeB entities (UserId). Does anyone know how to do this? It seems like it should be a common problem with Table Per Hierarchy mapping.
My entity classes are as follows:
public class Widget
{
public int Id { get; set; }
public string Name { get; set; }
}
class WidgetMap : EntityTypeConfiguration<Widget>
{
public WidgetMap()
{
ToTable("Widgets");
HasKey(w => w.Id);
Property(w => w.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(w => w.Name)
.IsRequired()
.HasMaxLength(75)
.IsUnicode(true);
}
}
public class WidgetTypeA : Widget
{
public int UserId { get; set; }
public virtual User User { get; set; }
public string Color { get; set; }
public int DepthLevel { get; set; }
}
class WidgetTypeAMap : EntityTypeConfiguration<WidgetTypeA>
{
public WidgetTypeAMap()
{
Map(w => w.Requires("WidgetTypeId").HasValue(1));
HasRequired(w => w.User)
.WithMany(u => u.WidgetTypeAs)
.HasForeignKey(w => w.UserId)
.WillCascadeOnDelete(false);
Property(w => w.Color)
.IsOptional()
.IsUnicode(true)
.HasMaxLength(75);
Property(w => w.DepthLevel)
.IsOptional();
}
}
public class WidgetTypeB : Widget
{
public int UserId { get; set; }
public virtual User User { get; set; }
}
class WidgetTypeBMap : EntityTypeConfiguration<WidgetTypeB>
{
public WidgetTypeBMap()
{
Map(w => w.Requires("WidgetTypeId").HasValue(2));
HasRequired(w => w.User)
.WithMany(u => u.WidgetTypeBs)
.HasForeignKey(w => w.UserId)
.WillCascadeOnDelete(false);
}
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public int Age { get; set; }
public virtual ICollection<WidgetTypeA> WidgetTypeAs { get; set; }
public virtual ICollection<WidgetTypeB> WidgetTypeBs { get; set; }
}
class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
ToTable("Users");
HasKey(u => u.Id);
Property(u => u.Username)
.IsRequired()
.HasMaxLength(75)
.IsUnicode(true);
Property(u => u.Age)
.IsRequired();
}
}
At any rate, I keep getting the error
Invalid column name 'UserId1'
when I try to perform the following operations:
using (var entities = new MyEntities())
{
User u = new User
{
Username = "Frank",
Age = 14
};
entities.Users.Add(u);
entities.SaveChanges();
WidgetTypeA wa1 = new WidgetTypeA
{
Name = "0SDF81",
UserId = u.Id,
DepthLevel = 6
};
entities.WidgetTypeAs.Add(wa1);
entities.SaveChanges();
}
Not sure if this can be fixed or not. I can always specify a second UserId foreign key for the Widgets table, but that seems pointless. Perhaps there's a way to do this using Fluent API?
You cannot map properties defined in different derived entities to the same column. That is limitation in EF. If your WidgetTypeA has UserId property and your WidgetTypeB has UserId property they must be different columns in the database. It should work if you move both UserId and User properties from derived types to the parent Widget type.
I know its a long way late, but hopefully may help other readers.
Although Ladislav was correct that using a mapped Foreign Key is not supported in EF6, I did find a useful workaround.
It is possible to define a computed column specification whose expression simply refers to the original column. Userid in the description above. This can be used as the discriminator for the TPH mapping. With this approach, the column need not be persisted, but can be used for TPH, with the original column being available for use as a foreign key.