One to many config in Entity Framework and Join Expressions - c#

I am using EF codefirst .I am confused on relationship in Entities .I have Two entities Student and Standard . Shown below
public class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public int StdandardId { get; set; }
}
public class Standard
{
public int StandardId { get; set; }
public string StandardName { get; set; }
public string Description { get; set; }
}
They have One to Many relationship.
I can do this by simple join expression like this
var list = StudentList.Join
(StandardList,c => c.StdandardId,o => o.StandardId,(c, o) => new
{
StudentId = c.StudentId,
StudentName = c.StudentName,
StandardName = o.StandardName
});
Then why should i configure forienkey One-to-Many relationship like
public class Student
{
public Student() { }
public int StudentId { get; set; }
public string StudentName { get; set; }
public int StdandardId { get; set; }
public virtual Standard Standard { get; set; }
}
public class Standard
{
public Standard()
{
Students = new List<Student>();
}
public int StandardId { get; set; }
public string StandardName { get; set; }
public string Description { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
Is there any key benifits.? Which one will perform well ?

The navigation property (Students) is an implicit join. but it's an outer join. If you join explicitly you can enforce an inner join, which will generally perform better. So do this if performance is critical.
So give yourself the opportunity to do both. Create navigation properties and join explicitly when necessary.
The benefit of navigation properties is a much more succinct syntax. e.g.
from standard in Standards
select new { standard.StandardName , NrOfStudents = standard.Students.Count() })
For this query you always want an outer join, because you'd also want to report the standards with zero students.
Or an implicit SelectMany:
from standard in Standards
where standard.StandardId == id
from student in standard.Students
select new { student. ... }
Navigation properties help you to carry out joins without this verbose join syntax.

This question can result in answers ranging to the size of essays.
I'll try to keep it essential. In short, Foreign Keys are used to ensure referential integrity.
In your select statement it might not make a difference, but think about update, insert and delete statements, and hurdles you'd have to take in order to cascade everything down to the last table.
Lets assume your Foreign Key constraint is set to Cascade. Whenever you make a change in your mastertable, the change is cascaded down to every child table. You'd have to manually join every table in your statement to implement the same.
If the constraint is set to Restrict you can not delete a Student as long as there are still Standards referencing to it. Again, You'd have to check this by hand in your statements every time.
You may very well be able to do this in your head, but you will make an error, and then you may be stuck with inconsistent data, when money is on the line.
And then there is comfort in EF.
I could join my data, but if there are Foreign Keys and tehrefore relationships in place I could write
var students = context.Students.Include(o => o.Enrollments);
and use it in my View with
#foreach(var enrollment in Model.Enrollments)
{
//...
}
In essence, this is not a problem related purely to Entity Framework, but to relational databases.
Have a look at ACID and Foreign Keys

Related

Trouble querying a collection of a collection

I'm having issues querying for a specific property using linq in EF.
To outline, Users have associated roles. Each Role has Associated groups. I'm just trying to get the MAMUserGroup property of all the groups associate with the user. I can easily get the associate Roles with .Include(), but am having trouble going the one extra level down to the associated MAMUserGroups.
User model:
public class User
{
[Display(Name = "SSO")]
[Required]
[StringLength(9, ErrorMessage = "SSO must be 9 numbers", MinimumLength = 9)]
public virtual string ID { get; set; }
[Display(Name = "First Name")]
[Required]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[Required]
public string LastName { get; set; }
[Display(Name = "MAM Roles")]
public ICollection<MAMRoleModel> MAMRoles { get; set; }
}
MAMRole Model:
public class MAMRoleModel
{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<MAMUserGroupModels> MAMUserGroups { get; set; }
}
MAM Group Model:
public class MAMUserGroupModels
{
public int ID { get; set; }
public string MAMUserGroup { get; set; }
}
I've tried
foreach(var bar in user.MAMRoles)
{
foreach(var foo in bar.MAMUserGroups)
{
//do something
}
}
But got null reference error. I've also just tried from haim770
var test = db.Users.Include(x => x.MAMRoles.Select(y=> y.MAMUserGroups));
but MAMUserGroup is a count of 0, so it's not seeing the reference.
I think the problem arises because you didn't declare your ICollection in the one-to-many relations in entity-framework as virtual. Declaring the ICollection virtual would solve it
To prevent future problems, consider to make your classes more entity-framework compliant. This diminishes the use of all kinds of attributes. Proper use of plurals and singulars improves readability of the queries which helps those who have to change the code in future.
The primary key 'ID' should not be virtual
In your one-to-many use the virtual ICollection on the one-side, and add add a reference to the one and the foreign key on your many-side
Consider using standard naming conventions. This helps entity-framework to define a model without you having to help it using all kinds of attributs.
Only deviate from the standard naming conventions if you really have to. In that case add Attributes fro primary key, foreign key, one-to-many relation etc, or consider using fluent API.
.
public class User
{
// Standard naming convention: automatic primary key
public string ID { get; set; }
// a user has many MAMRoles.
// standard naming convention: automatic one-to-many with proper foreign key
// declare the collection virtual!
public virtual ICollection<MAMRole> MAMRoles { get; set; }
}
public class MAMRole
{
// standard naming cause automatic primary key
public int ID { get; set; }
// a MAMRole belongs to one user (will automatically create foreign key)
// standard naming cause proper one-to-many with correct foreign key
public string UserId {get; set;}
public virtual User User {get; set;}
// A MamRole has many MAMUserGroupModels
// same one-to-many:
// again: don't forget to declare the collection virtual
public virtual ICollection<MAMUserGroupModel> MamUserGroupModels{ get; set; }
}
public class MAMUserGroupModel
{
// automatic primary key
public int ID {get; set;}
// a MAMUserGroupModel belongs to one MAMUser
// automatic foreign key
public int MAMUserId {get; set;}
public virtual MAMUser MAMUser {get; set;}
}
By the way. Entity framework knows it needs to get the value of a property as soon as you use it in your IQueryable. Only if you want to select property values and get them to local memory you'll need to use Include. This makes the query more efficient as only the used values are selected.
So if you only want to do something with the MamUserGroup inside your MamUserGroupModel, don't include anything:
In baby steps:
IQueryable<NamRole> mamRolesOfAllUsers = myDbContext.Users
.SelectMany(user => user.MamRoles);
IQueryable<MamRoleModel> mamUserGroupModelsOfAllUsers = mamRolesOfAllUsers
.SelectMany(mamRole => mamRole.MamUserGroupModels);
IQueryable<string> mamUserGroups = mamUserGroupModelsOfAllUsers
.Select(mamUserGroupModel => mamUserGroupModeModel.MamUserGroup;
Or in one statement
IQueryable<string> mamUserGroups = myDbContext.Users
.SelectMany(user => user.MamRoles)
.SelectMany(mamRole => mamRole.MamUserGroupModels)
.Select(mamUserGroupModel => mamUserGroupModel.MamUserGroup);
Note that until know I haven't communicated with the database yet. I've only created the expression of the query. The query will be done once the enumeration starts:
foreach(var userGroup in mamUserGroups)
{
...
}
Note the use of SelectMany instead of Select. Whenever you have collections of collections, use SelectMany to make it one collection. If you see yourself making a foreach within a foreach, this is a good indication that a SelectMany probably would have been a better choice.
Finally: did you see that because of proper use of plurals and singulars the queries are much more readable. Less changes of making mistakes when changes have to be implemented by someone else in future.

How to ask Automapper to grab related record by inner join on Id field which is not a foreign key?

I’ve been using Automapper for a while now, and so far it all works great. But recently I came across some “limitation” (or lack of my knowledge).
Let me give you a simplified example with two classes:
public class Consumable
{
public int ConsumableId { get; set; }
public string Description { get; set; }
public int SaleDepartmentId { get; set; }
}
public class SaleDepartment
{
public int SaleDepartmentId { get; set; }
public string Description { get; set; }
}
These two entities store the Id of SaleDepartment, but there is not foreign key linking SaleDepartment to Consumable (and I don’t want it as a key), however SaleDepartment has PrimaryKey on SaleDepartmentId
Now my DTO looks very similar
public class ConsumableDTO
{
public int ConsumableId { get; set; }
public string Description { get; set; }
public int SaleDepartmentId { get; set; }
}
Here is the mapping
Mapper.CreateMap<Consumable, ConsumableDTO>().ReverseMap();
So anytime I bring a Collection of ConsumableDTO’s I also want to bring the related SaleDepartment’s descriptions,
If there was a navigation property I would do something like this
Mapper.Map<ObservableCollection<Consumable>>
(context.Consumable.Project().To<ConsumableDTO>());
But because such a key does not exist, how would I tell the automapper to do inner join based on these Ids I have?
I have spent two days and I found a way of doing it, but I am not convinced that this the right way, and I am wondering whether I am missing a trick here and there is an easier or better way of achieving this with the automapper.
This is how I achieved getting the related record
var foo = new ObservableCollection<Consumable>(
(from c in context.Consumable.Project().To<ConsumableDTO>()
join sd in context.SaleDepartment on c.SaleDepartmentId equals sd.SaleDepartmentId
select new
{
consumable = c,
SaleDepartmentDescription = sd.Description
}).ToList()
.Select(p => Mapper.Map<ConsumableDTO, Consumable>(p.consumable, new Consumable()
{
SaleDepartmentDescription = p.SaleDepartmentDescription
})));
So, this will grab or consumable and then inner join saledeparments and select description form that inner join, but it seems like quite few steps, is there an easier way of telling the automapper, grab that related record based on this matching Id?
Thank you for your attention and time.
First, I'm assuming your DTO is meant to contain public string SaleDepartmentDescription { get; set; } as your question refers to it but it isn't actually there.
If you are NOT using EF migrations (a fair assumption since otherwise you'd just add the foreign key!), then you can do this by adding keys in your Entities - the keys don't actually need to present in the database for EF to join on them, this just tells EF to pretend that they are. (If you are using EF migrations then this approach will not work, as it will want to add the keys to the DB.)
public class Consumable
{
public int ConsumableId { get; set; }
public string Description { get; set; }
public int SaleDepartmentId { get; set; }
[ForeignKey("SaleDepartmentId")]
public virtual SaleDepartment SaleDepartment { get; set; }
}
Assuming your DTO does contain the string property SaleDepartmentDescription then AutoMapper will handle this automatically, though you should use ProjectTo to make more efficient database queries:
var mappedDTOs = context.Consumable.ProjectTo<ConsumableDTO>().ToList();

EF Code First foreign key definition / left join / navigation properties?

Let's say I have 3 tables:
[Table("Comments")]
public class Comment {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("Users")]
public int UserId { get; set; }
public virtual Users Users { get; set; }
public string Text { get; set; }
}
[Table("Users")]
public class Users {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
}
[Table("CommentAgree")]
public class CommentAgree {
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int CommentId { get; set; }
public int UserId { get; set; }
}
Users post comments and other users can 'agree' with that comment, a bit like Facebook's 'like' system. I'm using lambda for my queries and I can do:
var query = db.Comments.Select(c => new {
c.Id,
c.Users.UserName,
c.Text
});
How can I create a join to CommentAgree on Comment.Id = CommentAgree.CommentId? I could write the join in Lambda but I need it to be a left join as nobody may agree with the comment but I still want it to display.
I want to do it the right way, so I'm open to suggestions whether to do it by foreign keys, lambda joins, navigation properties... or something else?
Is this possible?
Thanks
The best approach is probably to use the features of Entity Framework and create navigation properties rather than explicitly using LINQ to perform the joins just for related data.
If your types are shaped just for the purposes of data access, then adding navigation properties to both ends of the relationship is probably a good idea, along with the foreign key properties that you already have.
The collection navigation property on Comment should implement ICollection (for example List<CommentAgree>), and you would have a reference navigation property of type Comment on the CommentAgree type.
You would then have to define the relationships in your mappings, either using data annotations or (preferably) the fluent API.
To load the related data, you could either use lazy loading, or eager loading (using the Include extension method), or use explicit loading from the entry information for the entity.

Entity Framework Mapping join table fields

I have a join table that has a field in it that I need to get into an Entity and have it updatable. I have the Table setup below, the column I need is "PersonelleID" in the "Account" table. Now, there may be multiple, so in that case, there's a concept of primary (think of it as if you were a student who went from one school to another, you'd have the same account but multiple schools).
Any idea how I can bring this into the Entity world? Generating the database ignores this field on the join table (probably because it doesn't know where to put it).
Trying to see what the best route to go is.
if you are using the Code-First approach you would create an entity for the join table that has foreign keys to both Account and School and your extra properties.
public class Account
{
public int AccountId { get; set; }
public string UserName { get; set;}
}
public class AccountSchool
{
[ForeignKey("Account")]
public int AccountId { get; set; }
[ForeignKey("School")]
public string CEEBCodeId { get; set; }
public string PersonelleID { get; set; }
}
public class School
{
[Key]
public string CEEBCodeId { get; set; }
public string Name { get; set;}
}
This is how I've done it and here's is an article explaining how to do it as well:
http://www.itq.nl/blogs/post/Code-First-Entity-Framework-Additional-properties-on-many-to-many-join-tables.aspx

MVC - Linq - Populate List<T> with Records in Another Table

I'm prototyping my first MVC application, it's a simple forum. I've done part of the domain model and I'm trying to figure out how to do something that's pretty basic in SQL alone, but I can't figure it out in my application. Here are my Entities:
[Table(Name="Users")]
public class User
{
[Column(IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.OnInsert)]
public int Id { get; set; }
[Column] public string Username { get; set; }
[Column] public string Password { get; set; }
[Column] public string PasswordHash { get; set; }
[Column] public string PasswordSalt { get; set; }
[Column] public string FirstName { get; set; }
[Column] public string LastName { get; set; }
public List<Forum> AllowedForums { get; set; }
[Column] public DateTime LastLogin { get; set; }
[Column] public DateTime MemberSince { get; set; }
}
[Table(Name="Forums")]
public class Forum
{
[Column(IsPrimaryKey=true, IsDbGenerated=true, AutoSync=AutoSync.OnInsert)]
public int Id { get; set; }
[Column] public int ParentId { get; set; }
[Column] public string Title { get; set; }
[Column] public string Description { get; set; }
[Column] public bool IsGlobal { get; set; }
[Column] public int DisplayOrder { get; set; }
}
I also have a linking table called AllowedForums that looks like:
userid forumid
1 4
In order to select the forums that a user is allowed to view and forums where IsGlobal == true I'd do this in SQL:
SELECT * FROM Forums
LEFT OUTER JOIN AllowedForums ON Forums.id = AllowedForums.Forumid
WHERE AllowedForums.Userid = 1
OR Forums.IsGlobal = 1
How should I populate the
public List<Forum> AllowedForums
field using C#/Linq to SQL?
Should AllowedForum be a value object with its own table mapping? That seems like overkill but I could easily join on it. I looked briefly at EntitySet but the simple example I saw didn't seem to fit. It feels like there should be an elegant way to get a collection of Forum objects for each User, but I can't come up with any. BTW, I'm new to C# & OO. I should also mention that since these are the early stages of the app, I'm open to changing the structure/relationships of the entities or tables if there's a better approach I'm not seeing.
You should have another Entity class (probably should be internal) that mirrors your AllowedForums table in the database. Now I'm assuming your User table and your Forums table both have PK/FK relationships to this AllowedForums table. Therefore, there should be an internal property on the User class that looks like this:
internal EntitySet<AllowedForums> AllowedForumsRelationships
{
get;set;
}
Or something like that. This should be on both the User and Forums class. Your AllowedForums class will have two properties on it. One for User and one for Forum. (If you use the LINQ to SQL designer, all this will happen for you automatically).
Once you have that, if you want to get all the AllowedForums for a user you can do something like this:
public IList<Forum> AllowedForums
{
get
{
var result = new List<Forum>();
foreach(var relationShip in this.AllowedForumsRelationships)
{
result.Add(relationShip.Forum);
return result;
}
}
}
This is some rough code I just banged out, and I'm not sure it's 100% accurate, but I think you'll get the idea. Basically you're dealing with a many to many relationship which is always a pain.
EDIT: I just messed with this idea with the Northwind Database with these tables:
Orders
OrderDetails
Products
There's a many to many relationship there: An order can have multiple products, and a product can belong to many orders. Now say you want to get all products for an order:
public partial class Order
{
public IList<Product> Products
{
get
{
var list = new List<Product>();
foreach (var item in this.Order_Details)
{
list.Add(item.Product);
}
return list;
}
}
}
That works, so it should work in the scenario you're talking about as well.
Something like:
var AllowedForums = from f in ForumsTable
join af in AllowedForumsTable on f.Id equals af.forumid into temp
from aft in temp.DefaultIfEmpty()
where (f.IsGlobal == 1 || aft.userid == 1)
select f;

Categories