are we able to Include(x=>x.Entity) then Select(x=> new Entity{ A=x.A }) - linq

I'm trying to call my CompanyCatalog table with its FileRepo table. There is a One to One relationship between them and i wanna apply when i Include(x=>x.FileRepo.Select(a=> new{ FileName=a.FileNAme} )) or any query like that.
Let me show to you my query in the bellow :
return TradeTurkDBContext.CompanyCatalog.Include(x=>x.FileRepo
.Select(x=> new FileRepo(FileName=x.FileName))).AsNoTracking().ToList();
I'm trying to do something like that. I'm asking is it possible or not ? if it's possible then how ?

So you have a table of CompanyCatalogs and a table of FileRepos. Every CompanyCatalog has exactly one FileRepo (one-to-one), namely the one that the foreign key refers to.
If you've followed the entity framework conventions, you will have classes similar to the following:
class CompanyCatalog
{
public int Id {get; set;}
public string Name {get; set;}
... // other properties
// every CompanyCatalog has one FileRepo, the one that the foreign key refers to
public int FileRepoId {get; set;}
public virtual FileRepo FileRepo {get; set;}
}
class FileRepo
{
public int Id {get; set;}
public string Name {get; set;}
... // other properties
// every FileRepo is the FileRepo of exactly one CompanyCatalog
// namely the one that the foreign key refers to
public int CompanyCatalogId {get; set;}
public virtual CompanyCatalog CompanyCatalog {get; set;}
}
This is enough for entity framework to detect your tables, the columns in the tables and the relations between the tables. If you had a one-to-many, you would have had a virtual ICollectioni<...> on the "one side". Only if you deviate from the conventions, for instance because you want other table names, or other column names, you need attributes or fluent API.
In entity framework the columns are represented by non-virtual properties. The virtual properties represent the relations between the tables (one-to-many, many-to-many, etc)
Foreign keys are columns in a table, hence they are not virtual. FileRepo is no column in the CompanyCatalogs table, hence it is declared virtual.
You want several properties of CompanyCatalogs, each with several properties of their FileRepos. You use Include for this. This is not necessary. A simple Select will do.
var companyCatalogs = dbContext.CompanyCatalogs
.Where(catalog => ...) // only if you don't want all CompanyCatalogs
.Select(companyCatalog => new
{
// Select only the CompanyCatalog properties that you plan to use:
Id = companyCatalog.Id,
Name = companyCatalog.Name,
...
// Select the FileRepo of this CompanyCatalog as one sub object
FileRepo = new
{
Date = companyCatalog.FileRepo.Date,
Title = companyCatalog.FileRepo.Title,
...
},
// if you want you can select the FileRepo properties one by one:
FileRepoDate = companyCatalog.FileRepo.Date,
FileRepoTitle = companyCatalog.FileRepo.Title,
});
Entity Framework knows your relations, and because you used the virtual properties of the class, it knows it has to perform a (Group-)Join.

Related

Querying many to many table in EF Core/LINQ [duplicate]

This question already has answers here:
Many-to-many query in Entity Framework 7
(4 answers)
Closed 2 years ago.
I have three tables: Posts, Tags and PostTags (link table between Post and Tag). How can I write a query to get all Posts by a TagId?
DB structure:
public class Post {
public string Id {get;set;}
public string Content {get;set;}
public List<PostTag> PostTags {get;set;}
}
public class Tag {
public string Id {get;set;}
public string Name {get;set;}
public List<PostTag> PostTags {get;set;}
}
public class PostTag
{
public string PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Relationships:
builder.Entity<PostTag>()
.HasKey(x => new { x.PostId, x.TagId });
builder.Entity<PostTag>()
.HasOne(st => st.Post)
.WithMany(s => s.PostTags)
.HasForeignKey(st => st.PostId);
builder.Entity<PostTag>()
.HasOne(st => st.Tag)
.WithMany(s => s.PostTags)
.HasForeignKey(st => st.TagId);
If you've followed the entity framework code first conventions, there are two methods to query "Posts with their Tags"
The easy way: Use the virtual ICollection<Tag> to get the tags of each post.
Do the (group-)join yourself.
Use the irtual ICollection
Your classes will be similar to the following:
class Post
{
public int Id {get; set;}
... // other properties
// every Post has zero or more Tags (many-to-many)
public virtual ICollection<Tag> Tags {get; set;}
}
class Tag
{
public int Id {get; set;}
... // other properties
// every Tag is used by zero or more Posts (many-to-many)
public virtual ICollection<Post> Posts {get; set;}
}
This is all that entity framework needs to know the many-to-many relation between Posts and Tags. You even don't have to mention the junction table, entity framework will create a standard table for you, and use it whenever needed. Only if you want non-standard names for tables and or columns, you need Attributes or fluent API.
In entity framework, the columns of the tables are represented by the non-virtual properties; the virtual properties represent the relations between the tables (one-to-many, many-to-many, ...)
To get all (or some) Posts, each with all (or some of) their Tables, you can use the virtual ICollection:
var postsWithTheirTags = dbContext.Posts
// only if you don't want all Posts:
.Where(post => ...)
.Select(post => new
{
// Select only the Post properties that you plan to use:
Id = post.Id,
Author = post.Author,
...
Tags = post.Tags.Select(tag => new
{
// again: only the properties that you plan to use
Id = tag.Id,
Text = tag.Text,
...
})
.ToList(),
});
Entity framework knows your relation and will automatically create a Group-join for you using the proper junction table.
This solutions seems to me the most natural one.
Do the GroupJoin yourself
For this you need to have access to the junction table, you'll have to mention it in your dbContext, and use fluent API to tell entity framework that this is the junction table for the many-to-many relation between Posts and Tags.
var postsWithTheirTags = dbContext.Posts.GroupJoin(dbContext.PostTags,
post => post.Id, // from every Post take the primary key
postTag => postTag.PostId // from every PostTag take the foreign key to Post
(post, postTagsOfThisPost) => new
{
// Post properties:
Id = post.Id,
Title = post.Title,
...
Tags = dbContext.Tags.Join(postTagsOfThisPost,
tag => tag.Id // from every Tag take the primary key
postTag => postTag.TagId // from every postTagOfThisPost take the foreign key
(tag, postTagfThisPostAndThisTag) => new
{
Id = tag.Id,
Text = tag.Text,
...
})
.ToList(),
});
You can try this:
public List<Posts> GetPosts(string needTagID)
{
var dataQuery = from tags in _db.Tags
where needTagID == tags.Id
join postTags in _db.PostTags on tags.Id equals postTags.TagId
join posts in _db.Posts on postTags.PostId equals posts.Id
select posts;
var data = dataQuery.ToList();
}

All items that match all the words in a collection

I have two lists: a list of type Person and a list of type profession. Both are many-to-many related.
In addition, I have a third list with some professions.
I would like to select all persons that match all the professions in the third list.
What would be LINQ/Lambda expression?
Thanks
The answer depends on how your sequence of Persons are connected to your sequence of Professions.
You are talking about Lists, but also about many-to-many relation, so I assume your lists are in fact tables in a relational database, with a joining table that remembers which Persons and Professions are related.
If you use entity framework, and you have set-up the many-to-many relationship correctly you don't need the third table:
class Person
{
public int Id {get; set;}
... // other properties
// every Person has zero or more Professions (many-to-many)
public virtual ICollection<Profession> Professions {get; set;}
}
class Profession
{
public int Id {get; set;}
... // other properties
// every Profession has zero or more Persons (many-to-many)
public virtual ICollection<Person> Persons {get; set;}
}
class MyDbContext : DbContext
{
public DbSet<Person> Persons {get; set;}
public DbSet<Profession> Professions {get; set;}
}
That is all!
Entity Framework will recognize that you are modeling a many-to-many relationship and will create the third table for it. You don't need this third table, just access the ICollections, and entity framework will automatically perform the required joins with the third table.
using (var dbContext = new MyDbContext())
{
IEnumerable<Profession> professionList = ... // the third list
// Keep only the persons where have exactly all Professions from the profession list
// do this by checking the Ids of the professions in the list
IEnumerable<int> professionIds = professions
.Select(profession => profession.Id)
.OrderBy(id => id);
var personsWithProfessions = dbContext.Persons
// keep only persons that have the same Profession Ids as professionIds
// first extract the the profession Ids the person has
.Where(person => person.Professions
.Select(profession => profession.Id)
// order this in ascending order
.OrderBy(id => id)
// check if equal to professionIds:
.SequenceEqual(professionIds))
If you are not using Entity Framework, or the classes are not set-up properly with the virtual ICollection, you'll have to do the join between Persons and Professions yourself
Assuming you have a joining table that joins your Persons and Professions:
class Person_Profession
{
public int Id {get; set;}
public int PersonId {get; set;}
public int ProfessionId {get; set;}
}
IQueryable<Person_Profession> Person_Profession_Table = ...
First group every person with all ProfessionIds in the Person_Profession_Table.
var personsWithProfessionIds = Persons.GroupJoin(person_profession_table,
person => person.Id,
personProfession => personProfession.PersonId,
person, matchingJoiningItems => new
{
Person = person,
ProfessionIds = matchingJoiningItems
.Select(matchingJoiningItem => matchingJoiningItem.ProfessionId)
.OrderBy(id => id)
.ToList(),
})
In words: take the two tables: Persons and PersonProfessions. From every person take the Id, from every personProfession element take the PersonId, for every person and all matching personProfessions make one new object: this object contains the matching Person, and all ProfessionIds of the matching joiningItems.
From these Persons with their ProfessionIds, keep only those Persons that have all ProfessionIds in your third list
IEnumerable<int> professionIds = professions
.Select(profession => profession.Id)
.OrderBy(id => id);
IEnumerable<Person> matchingPersons = personsWithProfessionIds
.Where(personWithProfessionId => personWithProfessioinId.ProfessionIds
.SequenceEqual(professiondIds))
.Select(personWithProfessionId => perfonWithProfessionId.Person);
Assuming your Lists are related by containing member Lists of the other type,
var AllPersons = new List<Person>();
var AllProfessions = new List<Profession>();
var desiredProfessions = new List<Profession>();
var findPersons = from p in AllPersons
where p.Professions.Any(pp => desiredProfessions.Contains(pp))
select p;

Entity Splitting For One-To-Many table relationships

Following this article (What are best practices for multi-language database design?), I have all my database tables splitted in two: the first table contains only language-neutral data (primary key, etc.) and the second table contains one record per language, containing the localized data plus the ISO code of the language. The relationship between the two tables is one to many.
Here a screenshot of the datamodel: https://dl.dropboxusercontent.com/u/17099565/datamodel.jpg
Because the website has 8 languages, for each record in table "CourseCategory" I have 8 record in table "CourseCategoryContents". The same happens with "Course" and "CourseContent"
Then I use Entity Splitting in order to have only one entity for the Course Category and one entity for the Course:
public class CourseCategoryConfiguration : EntityTypeConfiguration<WebCourseCategory>
{
public CourseCategoryConfiguration()
{
Map(m =>
{
m.Properties(i => new { i.Id, i.Order, i.Online });
m.ToTable("CourseCategories");
});
Map(m =>
{
m.Properties(i => new { i.LanguageCode, i.Name, i.Permalink, i.Text, i.MetaTitle, i.MetaDescription, i.MetaKeywords });
m.ToTable("CourseCategoryContents");
});
}
}
public class CourseConfiguration : EntityTypeConfiguration<WebCourse>
{
public CourseConfiguration()
{
Map(m =>
{
m.Properties(i => new { i.Id, i.CategoryId, i.Order, i.Label, i.ThumbnailUrl, i.HeaderImageUrl });
m.ToTable("Courses");
});
Map(m =>
{
m.Properties(i => new { i.LanguageCode, i.Name, i.Permalink, i.Text, i.MetaTitle, i.MetaDescription, i.MetaKeywords, i.Online });
m.ToTable("CourseContents");
});
}
}
Then to retrive the courses in a desired language including their category I do this:
using (WebContext dbContext = new WebContext())
{
// all courses of all categories in the desired language
return dbContext.Courses
.Include(course => course.Category)
.Where(course => course.LanguageCode == lan
&& course.Category.LanguageCode == lan)
.ToList();
}
}
Entity splitting works fine with one-to-one relationships, but here I have one-to-many relationships.
The website has contents (CourseCategories and Courses) in 3 languages ("en", "de", "fr").
EF correctly returns all the Courses with their Category in the right language (eg. in english), but returns each record 3 times. This is because I have the CourseCategory in 3 languages too.
The only one working solution I came up is avoiding using ".Include(Category)", getting all the courses in the desired language in first, then, in a foreach cycle, for each Course retriving its Category in language. I don't like this lazy loading approach, I would like to retrive all the desired data in one shot.
Thanks!
The best solution is to map tables to the model as it then in your model Course class will have a navigation property ICollection<CourseCategoryContent>.
In this case you just project this model to DTO or ViewModel "according to your application design"
e.g.
Your model will look like this
public class Course
{
public int Id {get; set;}
public int Order {get; set;}
public ICollection<CourseCategoryContent> CourseCategoryContents {get; set;}
}
public class CourseCategoryContent
{
public string LanguageId {get; set;}
public string Name {get; set;}
}
Then just create new DTO or ViewModel like :
public class CourseDTO
{
public int Id {get; set;}
public int Order {get; set;}
public string Name {get; set;}
}
Finally do the projection
public IQueryable<CourseDTO> GetCourseDTOQuery ()
{
return dbContext.Courses.Select(x=>new CourseDTO{
Id = x.Id,
Order = x.Order,
Name = x.CourseCategoryContents.FirstOrDefault(lang => lang.LanguageId == lang).Name,
});
}
And note that the return type is IQueryable so you could do any filter, Order or grouping operation on it before hitting the database.
hope this helped
No fix-all answer i'm afraid, every way has a compromise.
I've used both the database approach (10+ language dependent tables) and the resource file approach in fairly large projects, if the data is static and doesn't change (i.e you don't charge a different price or whatever) I would definately consider abstracting language away from your database model and using Resource keys then loading your data from files.
The reason or this is the problem you are experiencing right now where you can't filter includes (this may have changed in EF6 perhaps? I know it's on the list of things to do). You might be able to get away with reading it into memory and filtering them though like you're doing but this meant it wasn't very performant for us and I had to write Stored Procedures that I just passed the iso language and executed in EF.
From a maintenance point of view it was easier as well, for the DB project I had to write an admin console so people could log on and edit values for different languages etc. Using resource files I just copy-pasted the values into excel and emailed them to the people we use to translate.
It depends on the complexity of your project and what you prefer, i'd still consider both approaches in future.
TLDR: options that i've found are:
1) filter in memory
2) lazy load with filter
3) write stored procedure to EF and map that result
4) use resources instead
Hope this helps
EDIT: After looking at diagram it looks like you may need to search against the language dependant values? In that case resources probably won't work. If you're just letting them navigate off a menu then you're good to go.

Defining a one-to-one-or-zero relationship in Entity Framework with Code First

I know there are a lot of questions around on this subject, but I've not managed to find one that actually explains how to solve my particular problem. Which I suppose means that it might be insoluble (I think it might be 'backwards' to EF's way of thinking), but I have to ask.
I have a model with three (abbreviated) POCOs as so:
[Table("People")]
public class Person {
public int PersonID { get; set; }
[Required]
public string PersonName { get; set; }
}
public class Location {
public int LocationID { get; set; }
public int LocationTypeID { get; set; }
public virtual LocationType LocationType { get; set; }
}
public class Van : Location {
public int PartyID { get; set; }
public virtual Party Party { get; set; }
}
These are backed by (abbreviated) database tables (we write these by hand):
CREATE TABLE People (
PersonID INTEGER IDENTITY NOT NULL,
PersonName VARCHAR(255) NOT NULL,
PRIMARY KEY (PersonID)
)
CREATE TABLE Locations (
LocationID INTEGER IDENTITY NOT NULL,
LocationTypeID INTEGER NOT NULL,
FOREIGN KEY (LocationTypeID) REFERENCES LocationTypes(LocationTypeID)
)
CREATE TABLE Vans (
LocationID INTEGER NOT NULL,
PersonID INTEGER NOT NULL,
PRIMARY KEY (LocationID),
FOREIGN KEY (LocationID) REFERENCES Locations(LocationID),
FOREIGN KEY (PersonID) REFERENCES People(PersonID)
)
You can probably imagine what LocationTypes looks like.
Locations is the root of a table-per-type hierarchy - there are also check constraints in place to enforce this. Vans are a kind of location, as are other things irrelevant here like Warehouse.
Now, a Van belongs to a Person, in that we issue a van to an employee and it's their responsibility to fill it up with fuel, not crash it, take it to customer sites and order more stuff when they've used up all their supply of screws, drill bits and armoured DC cable. However, not every Person has a van (some of them work in pairs in one van), and the Person table doesn't have a foreign key which points to the Van - it's the other way around. This is in some sense a historical accident, but it models the situation quite neatly because while a Person doesn't have to have a Van, a Van most assuredly has to have a person.
So to my question: how do I get Person to have a navigation property with their Van in it?
public virtual Van Van { get; set; }
I've done a lot of playing around with data annotations and the fluent API, and the closest I've got is this in OnModelCreating:
modelBuilder.Entity<Van>()
.HasRequired(v => v.Person)
.WithOptional(p => p.Van);
Unfortunately this tries to populate the Van property with a proxy that yields a Location object. It might even be the right Location object (I haven't been able to check), but it's not realised that it should be looking for vans. I do suspect, however, that it might be trying to match PersonID against LocationID when it does the lookup - without the Fluent API mapping, I just get no vans at all, which is what I'd expect (all PersonID values are lower than the lowest LocationID values which correspond to vans so couldn't possibly find anything).
This would no doubt be quite easy if Person had a nullable foreign key to Van, but then we'd have foreign keys in both directions, and if we took the one out of Van then we'd not be modelling the absolutely essential constraint that a Van has a Person.
So, I suppose, Van owns this relationship, and the Van property on Person is an inverse navigation property, but it seems EF isn't very good at this kind of trick with one-to-ones even if one end is optional. Is there a way to make it work, or do I have to accept a compromise?
We generally refuse to compromise the database model for the sake of Entity Framework's missing features. What I really need is a way to tell EF that the Van property on Person can be populated by joining to Vans on Vans.PersonID = Person.PersonID.
The problem is that this is at the moment not supported. You mentioned that you didn't managed to find any question where would be your problem solved. I wonder if you find any question mentioning that EF doesn't support unique constraints / candidate key which is absolutely necessary to solve this type of one-to-one relations.
In database one-to-one relation can be achieved only if FK in dependent table is unique. This can be done by two ways: placing unique constraint (index) on FK column in dependent table or using PK in dependent table as FK to principal table.
EF enforces same rules for referential integrity as database but in case of one-to-one relationships and lack of support for unique constraint it doesn't support the former way. EF can model one-to-one relationship only by using PK of dependent table as FK to principal table.
You can vote for support of Unique constraints / candidate keys on Data UserVoice.
How to solve your particular issue? By cheating EF. Let EF think that you have one-to-many relation and place unique constraint on PersonID in Van table. Than update your Person like this:
[Table("People")]
public class Person {
public int PersonID { get; set; }
[Required]
public string PersonName { get; set; }
public virtual ICollection<Van> Vans { get; set; }
[NotMapped]
public Van Van
{
get { return Vans.FirstOrDefault(); }
set
{
Vans.Clear();
if (value != null)
{
Vans.Add(value);
}
}
}
}
It is pretty ugly workaround because Vans collection is still public. You can play with its visibility but make sure you understand few things:
Once Vans is not public you must map it in OnModelCreating and for that context must be able to see the property or you must provide mapping configuration which does that. Check this and this for some more information.
Vans property must not break rules for proxy creation to support lazy loading
Eager loading must use Vans property

Show display values for a foreign key property in a model

I have a model that looks kinda like this:
public class Notes
{
public int NoteID {get; set;}
public string Note {get; set;}
public int CustomerID {get; set;}
{
On the Notes Details view, I would like to be able to show Customer Name instead of CustomerID. Obviously if this were a Create or Edit view I use a dropdown list.
However I am not sure how to show the value and not the ID in a read only Details view.
Thanks!
Code First is mainly... code, not Database logic.
So, instead of having the Foreign Keys (like CustomerID) in your models (it's also possible, and sometimes needed, but not always), you'll be more confortable having a reference property
public virtual Customer Customer {get;set;}
So in your view having Notes as Model, you could simply use
#Html.DisplayFor(m => m.Customer.Name);
When you retrieve your Notes entity, don't forget to include the related entities / properties needed for your View / ViewModel (I let you read about lazy loading)
You can try this:
var applicationDbContext = _context.Notes.Include(n => n.Custumer);
return View(await applicationDbContext.ToListAsync());
This code must be in your controller, in the Index method or whatever you named it.
And this is in view.
#Html.DisplayFor(modelItem => item.Customer.Name)

Resources