I have classes as follows:
public class File {
public Guid Id { get; set; }
}
public class Customer {
public Guid Id { get; set; }
public List<File> Files { get; set; }
}
And Customer.Files is setup as a bag of components (not entities). Is there any way I can make a QueryOver that will return a customer who has a file with a given ID using QueryOver syntax in NHibernate?
it should be as easy as
session.QueryOver<Customer>()
.JoinQueryOver<File>(c => c.Files)
.Where(f => f.Id == id)
.List();
Related
I have a project where an .Include does not generate an INNER JOIN, while the INNER JOIN is generated in other projects using the same DbContext and code.
I am using EF Core Code-First. The DbContext is shared with other projects through NuGet.
Below are the two classes in question and their configuration in the DbContext:
[Table("Project", Schema = "ProjectManagement")]
public class AxosoftProject
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AxosoftId { get; set; }
public int? AxosoftParentId { get; set; }
public AxosoftProject AxosoftParent { get; set; }
public List<AxosoftProject> Children { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public string Description { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public DateTime UpdateDate { get; set; }
public ICollection<AxosoftItem> Items { get; set; }
}
[Table("Item", Schema = "ProjectManagement")]
public class AxosoftItem
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AxosoftId { get; set; }
public int AxosoftProjectId { get; set; }
public AxosoftProject AxosoftProject { get; set; }
public string Name { get; set; }
public string ItemType { get; set; }
public string SubItemType { get; set; }
public decimal PercentComplete { get; set; }
public DateTime UpdateDate { get; set; }
}
DbContext:
modelBuilder.Entity<AxosoftProject>().HasKey(x => x.AxosoftId);
modelBuilder.Entity<AxosoftProject>().HasOne(x => x.AxosoftParent).WithMany(x => x.Children);
modelBuilder.Entity<AxosoftProject>().HasMany(x => x.Items);
modelBuilder.Entity<AxosoftItem>().HasKey(x => x.AxosoftId);
modelBuilder.Entity<AxosoftItem>().HasOne(x => x.AxosoftProject);
Code to query all projects and their items:
IMyEntities MyEntities = new MyEntities("Server=localhost;Database=MyEntities;Trusted_Connection=True;MultipleActiveResultSets=true");
var projects = MyEntities.AxosoftProjects.Include(x => x.Items).ToList();
var listwithitems = projects.Where(x => x.Items != null).ToList();
Depending on the project that I execute above code in, it will either do an INNER JOIN or no INNER JOIN at all. I tried deleting the whole project where above code does not perform an INNER JOIN, cloning it from the repository afterwards to start from fresh, still no INNER JOIN.
How do I go about debugging this further? I suspect the project where it doesn't work may be using an old version of the DLL's even if it looks like it's pointing to the correct ones.
UPDATE
To verify that the include is indeed not working, I did an additional test. If I load the AxosoftItems into the DbContext seperately, then EF will populate the Items collections for each AxosoftProject. Example:
var projects = MyEntities.AxosoftProjects.Include(x => x.Items).ToList();
var listwithitems1 = projects.Where(x => x.Items != null).ToList(); // 0 items
var items = MyEntities.AxosoftItems.ToList();
var listwithitems2 = projects.Where(x => x.Items != null).ToList(); // 41 items
UPDATE 2
The code and reference to EF Core was placed in an ASP.NET MVC project. Moving the code to a separate class library "fixes" the issue, the INNER JOIN is not ignored, and the code is exactly the same. I intend to download the source code and debug my way through, to figure out why the Include is being ignored in the MVC project.
I am using Entity Framework Core following Chris Sakell's blog here.
He uses generics to manage his repositories and also a base repository that he uses for all the other repositories.
Part of the base repository has the the following code for the retrieval of a single entity that also downloads related entities using the includeProperties option. Here is the generic code for a retrieving a single item.
public T GetSingle(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> query = _context.Set<T>();
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query.Where(predicate).FirstOrDefault();
}
I am using it on a client table that has many jobs attached to it.
This is how I structured my code.
public ClientDetailsViewModel GetClientDetails(int id)
{
Client _client = _clientRepository
.GetSingle(c => c.Id == id, c => c.Creator, c => c.Jobs, c => c.State);
if(_client != null)
{
ClientDetailsViewModel _clientDetailsVM = mapClientDetailsToVM(_client);
return _clientDetailsVM;
}
else
{
return null;
}
}
The line:
.GetSingle(c => c.Id == id, c => c.Creator, c => c.Jobs, c => c.State);
successfully retrieves values for creator state and job.
However, nothing is retrieved for those related entities associated with the "jobs".
In particuar, JobVisits is a collection of visits to jobs.
For completeness I am adding the "job" and "jobvisit" entities below
public class Job : IEntityBase
{
public int Id { get; set; }
public int? ClientId { get; set; }
public Client Client { get; set; }
public int? JobVisitId { get; set; }
public ICollection<JobVisit> JobVisits { get; set; }
public int? JobTypeId { get; set; }
public JobType JobType { get; set; }
public int? WarrantyStatusId { get; set; }
public WarrantyStatus WarrantyStatus { get; set; }
public int? StatusId { get; set; }
public Status Status { get; set; }
public int? BrandId { get; set; }
public Brand Brand { get; set; }
public int CreatorId { get; set; }
public User Creator { get; set; }
....
}
public class JobVisit : IEntityBase
{
...
public int? JobId { get; set; }
public Job Job { get; set; }
public int? JobVisitTypeId { get; set; }
public JobVisitType VisitType { get; set; }
}
My question is, how do I modify the repository code above and my GetSingle use so that I can also load the related enitities JobVisit collection and the other related single entities Brand and JobType?
It is intended that navigation properties are not necessary retrieved for associated with the "jobs". That is why some properties are null. By default the .Include(property); goes only 1-level deep and that is a good thing. It prevents your query from fetching all the data of your database.
If you want to include multiple levels, you should use .ThenInclude(property) after .Include(property). From the documentation:
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ToList();
}
My advice is that your method public T GetSingle(...) is nice and I would not change it in order to include deeper levels. Instead of that, you can simply use explicit loading. From the documentation:
using (var context = new BloggingContext())
{
var blog = context.Blogs
.Single(b => b.BlogId == 1);
context.Entry(blog)
.Collection(b => b.Posts)
.Load();
context.Entry(blog)
.Reference(b => b.Owner)
.Load();
}
I have in my model Student that have a collection of all his subjects and every subject have collection of Educational matches.
public class Subject
{
public int SubjectID { get; set; }
public string SubjectName {get; set; }
public ICollection<Student> { get; set; }
}
public class EducationalMatches
{
public int EducationalMatchesID { get; set; }
public int Number { get; set; }
public string Name { get; set; }
}
public class Student
{
public int StudentID { get; set; }
public Icollection<AllStudentSubjects> AllStudentSubjects{ get; set; }
}
public class AllStudentSubject
{
public int AllStudentSubjectID { get; set; }
public Subject Subject { get; set; }
public ICollection<EducationalMatches> Educations { get; set; }
}
I'm expecting that in DB a table that looks like that will appear:
tableID
StudentID
SubjectID
EducationMatchesID
but no such table appears.
anyone have an idea?
Having a model is not enough, you need to override OnModelCreating method (it is empty by default). Plus EF wants to have 'reverse' property, for example, if Student has a collection of Subjects, Subject should have a collection of Students (for many-to-many relationship)
In your case for AllStudentSubject it should be like this (did not test)
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<AllStudentSubject>()
.HasKey(a => a.AllStudentSubjectID ) //Primary key, I prefer [Key] attribute, but this also works
.HasRequired(a => a.Student) //Property in AllStudentSubject
.WithMany(s => s.AllStudentSubjects) // Collection property in Student class
.HasForeignKey(a => a.StudentId)//Second property in AllStudentSubject
//For Student, you do not have to write this all again, just the primary key
modelBuilder.Entity<Student>()
.HasKey(a => a.StudentId ) //I like to move 'simple' declarations like this to the top
}
For other two entities you have to do the same.
Here`s a great article with all concepts explained
I have two tables which have primary and foriegn key concept. I want to get the combined data on behalf of those keys. i don't know how to bind both the table into single model and display it into view.
Model
public class TVSerialModel
{
public Int32 Serial_ID { get; set; } // primary key
public string Serial_Name { get; set; }
public int? Release_Year { get; set; }
}
public class TVSerialEpisodeModel
{
public Int64 Video_ID { get; set; }
public Int32 Serial_ID { get; set; }// foriegn key
public string Episode_Name { get; set; }
public string Description { get; set; }
public DateTime Uploaded_Time { get; set; }
}
public class TVSerial_Episode_VM
{
public IEnumerable<TVSerialEpisodeModel> tvserialEpisode { get; set; }
public IEnumerable<TVSerialModel> Tvserial { get; set; }
}
Controller
public ActionResult NewEpisodeReleased()
{
cDBContext tvContext = new cDBContext();
TVSerial_Episode_VM tves=new TVSerial_Episode_VM();
tves= tvContext.dbTvSerialEpisodes.
Join(tvContext.dbTvSerials, p => p.Serial_ID, r => r.Serial_ID,(p, r) => new { p, r }).
Select(o => new TVSerial_Episode_VM
{ ****what should i write here to get all columns from both table**** }).
Take(9).ToList();
return View(tves);
}
Expected Result
If TVSerialEpisode has a property TVSerial, you can just dot through your foreign keys.
cDBContext.dbTvSerialEpisode
.Select(t =>
new {
t.TVSerial.Serial_ID,
t.TVSerial.Serial_Name,
t.Episode_Name
})
.Take(9)
.ToList();
You need to improve little bit the models you used with EF. You must include the reference object in model.
Like this
public virtual TVSerialModel TVSerialModel { get; set; }
in main table. This way you can select referred table too.
EF Include
public ActionResult NewEpisodeReleased()
{
cDBContext tvContext = new cDBContext();
TVSerial_Episode_VM tves=new TVSerial_Episode_VM();
tves= tvContext.dbTvSerialEpisodes.Include("TVSerialEpisodeModel")
.Include("TVSerialModel").ToList();
return View(tves);
}
I'm new to MVC and EF code first. I'm in struggle to model a real-estate company DB model using EF code-first approach and I did some exercises as well as reading some online tutorials.
First thing I have a customers table that would be in relation with one or more properties he/she has registered as it's owner to sell or to rent, I was wondering if it is possible to have some sub classes inside a model class for registered properties as below:
public Property
{
public int PropertyID { get; set; }
public bool IsforSale { get; set; }
public bool IsforRent { get; set; }
public class Apartment{
public int ApartmentID { get; set; }
public int AptSqureMeter { get; set; }
. . .
. . .
}
public class Villa{
public int VillaID { get; set; }
public int VillaSqureMeter { get; set; }
. . .
. . .
}
and also other sub-classes for other types of properties
}
If the answer is Yes, then how should I declare the relations using data annotation or Fluent API, and then please help me how to update both Customers table and Property table with the customer information and property info at the same time?
thanks for your answer in advance.
As #Esteban already provided you with a pretty detailed answer on how to design your POCOs and manage the relationship between them, I will only focus on that part of your question:
how should I declare the relations using data annotation or Fluent API
First of all, you should know that certain model configurations can only be done using the fluent API, here's a non exhaustive list:
The precision of a DateTime property
The precision and scale of numeric properties
A String or Binary property as fixed-length
A String property as non-unicode
The on-delete behavior of relationships
Advanced mapping strategies
That said, I'm not telling you to use Fluent API instead of Data Annotation :-)
As you seem to work on an MVC application, you should keep in mind that Data Annotation attributes will be understood and processed by both by Entity Framework and by MVC for validation purposes. But MVC won't understand the Fluent API configuration!
Both your Villa and Apartment classes have similar properties, if they are the same but as it's type, you could create an enum for that.
public enum PropertyType {
Apartment = 1,
Villa
}
public class Property {
public int PropertyID { get; set; }
public bool IsforSale { get; set; }
public bool IsforRent { get; set; }
public PropertyType PropertyType { get; set; }
public int SquareMeter { get; set; }
}
This way of modelating objects is refered as plain old clr object or POCO for short.
Assume this model:
public class User {
public int UserId { get; set; }
public string Username { get; set; }
public virtual List<Role> Roles { get; set; }
}
public class Role {
public int RoleId { get; set; }
public string Name { get; set; }
public virtual List<User> Users { get; set; }
}
Creating relations with fluent api:
Mapping many to many
On your OnModelCreating method (you'll get this virtual method when deriving from DbContext):
protected override void OnModelCreating(DbModelBuilder builder) {
// Map models/table
builder.Entity<User>().ToTable("Users");
builder.Entity<Role>().ToTable("Roles");
// Map properties/columns
builder.Entity<User>().Property(q => q.UserId).HasColumnName("UserId");
builder.Entity<User>().Property(q => q.Username).HasColumnName("Username");
builder.Entity<Role>().Property(q => q.RoleId).HasColumnName("RoleId");
builder.Entity<Role>().Property(q => q.Name).HasColumnName("Name");
// Map primary keys
builder.Entity<User>().HasKey(q => q.UserId);
builder.Entity<Role>().HasKey(q => q.RoleId);
// Map foreign keys/navigation properties
// in this case is a many to many relationship
modelBuilder.Entity<User>()
.HasMany(q => q.Roles)
.WithMany(q => q.Users)
.Map(
q => {
q.ToTable("UserRoles");
q.MapLeftKey("UserId");
q.MapRightKey("RoleId");
});
Mapping different types of relationships with fluent api:
One to zero or one:
Given this model:
public class MenuItem {
public int MenuItemId { get; set; }
public string Name { get; set; }
public int? ParentMenuItemId { get; set; }
public MenuItem ParentMenuItem { get; set; }
}
And you want to express this relationship, you could do this inside your OnModelCreating method:
builder.Entity<MenuItem>()
.HasOptional(q => q.ParentMenuItem)
.WithMany()
.HasForeignKey(q => q.ParentMenuItemId);
One to many
Given this model:
public class Country {
public int CountryId { get; set; }
public string Name { get; set; }
public virtual List<Province> Provinces { get; set; }
}
public class Province {
public int ProvinceId { get; set; }
public string Name { get; set; }
public int CountryId { get; set; }
public Country Country { get; set; }
}
You now might want to express this almost obvious relationship. You could to as follows:
builder.Entity<Province>()
.HasRequired(q => q.Country)
.WithMany(q => q.Provinces)
.HasForeignKey(q => q.CountryId);
Here are two useful links from MSDN for further info:
Configuring Relationships with the Fluent API.
Code First Relationships Fluent API.
EDIT:
I forgot to mention how to create a many to many relationship with additional properties, in this case EF will NOT handle the creation of the join table.
Given this model:
public class User {
public int UserId { get; set; }
public string Username { get; set; }
public virtual List<Role> Roles { get; set; }
pubilc virtual List<UserEmail> UserEmails { get; set; }
}
pubilc class Email {
public int EmailId { get; set; }
public string Address { get; set; }
public List<UserEmail> UserEmails { get; set; }
}
public class UserEmail {
public int UserId { get; set; }
public int EmailId { get; set; }
public bool IsPrimary { get; set; }
public User User { get; set; }
public Email Email { get; set; }
}
Now that we've added a new property into our join table ef will not handle this new table.
We can achieve this using the fluent api in this case:
builder.Entity<UserEmail>()
.HasKey( q => new {
q.UserId, q.EmailId
});
builder.Entity<UserEmail>()
.HasRequired(q => q.User)
.WithMany(q => q.UserEmails)
.HasForeignKey(q => q.EmailId);
builder.Entity<UserEmail>()
.HasRequired(q => q.Email)
.WithMany(q => q.UserEmails)
.HasForeignKey(q => q.UserId);