One View for multiple models? - asp.net-mvc-4

If you need to create one View from various entity (models), is it best to create a separate class as a ViewModel containing the specific properties that you need or is it better to create another entity with the specific properties and associate that entity with the rest of the entities in the ORM designer?

You can merge any number of models into one model by declaring them a property of the main m odel. Suppose that you have the following models:
public class Student
{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int SchoolID { get; set; }
public virtual School StudentSchool { get; set; }
}
public class School
{
public School()
{
this.Students = new HashSet<Student>();
}
public int SchoolID { get; set; }
public string ASchoolName { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
If you just set the Student class as your view model, you can retrive the School of your student and in this case you doesn't need to do anything.
However we suppose that you also need all schools and all students in your view as your view model. To do this, create another class and add the above classes as its properties:
public class MyModel
{
List<Student> MyStudents { get; set; }
List<School> MySchools { get; set; }
}
You can create any complex model you need by this approach...

Related

How to retrieve the objects participating in a many-to-many relationship?

I have a many-to-many relationship between User(Contributor) and TaskType. I want to assign to a variable only TaskTypes that have the current user among their contributors. Obviously, I can somehow do this using the functionality of the Entity Framework. But how? I use asp.net core 3.
Below I try unsuccessfully to do it:
public IQueryable<TaskType> ContributedTaskTypes
{
get
{
// This code doesn't work!
return _dbContext.TaskTypes.Where(t => t.Contributors.Contains(c => c.UserId == CurrentUserId));
}
}
Below are definitions of all models involved in this relationship:
public class TaskType
{
public int Id { get; set; }
public string UserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public virtual List<Contribution> Contributors { get; set; }
}
public class Contribution
{
public int Id { get; set; }
public string UserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public int TaskTypeId { get; set; }
public TaskType TaskType { get; set; }
}
public class ApplicationUser : IdentityUser
{
public virtual List<Contribution> ContributedToTaskTypes { get; set; }
}
For those queries it is always easiest to do queries where you can dot to the result.
Here is the query with sql-like syntax
from row in _dbContext.Contribution
where row.UserId == CurrentUserId
select row.TaskType
By selecting row.TaskType instead of just row you get it correct entity.
Is that Contributors property retrieved correctly from DB? if it is not you must call Include() method to load/refer relational referenced entities
_dbContext.TaskTypes.Include(p=>p.Contributors).Where(..
more: https://learn.microsoft.com/en-us/ef/core/querying/related-data
In Addition, if EF Core Table Relation is not correctly defined, you should follow
this instruction: https://www.entityframeworktutorial.net/efcore/configure-many-to-many-relationship-in-ef-core.aspx

EF Core only creates one self-referencing foreign key instead of 2

I am trying to create a model to represent missions in a game. When a mission is completed, another mission is played depending on which team won the mission. For example Mission1, if TeamA wins then you play Mission2, if TeamB wins then you play Mission3.
For each mission I want two self-referencing columns, TeamAWinMission and TeamBWinMission, which hold the foreign key to another mission.
The migrations only seem to recognise a single self-reference in the model.
This will create a column for TeamAWinMission:
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
public Mission TeamAWinMission{ get; set; }
}
This will only create a column for TeamBWinMission:
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
public Mission TeamAWinMission{ get; set; }
public Mission TeamBWinMission{ get; set; }
}
How can I make the migration generate columns/foreign keys for both of these?
Edit: Was able to resolve this with InverseProperty from https://stackoverflow.com/a/46832490/11575271
It seems that there is one-to-one self-reference relationship in Mission model , you could try to define the [ForeignKey]in your model like below :
public class Mission
{
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("TeamAWinMission")]
public int? TeamAWinMissionId { get; set; }
public Mission TeamAWinMission { get; set; }
[ForeignKey("TeamBWinMission")]
public int? TeamBWinMissionId { get; set; }
public Mission TeamBWinMission { get; set; }
}
Then use Include attribute to load related data in the controller :
public IActionResult Mission()
{
var result = _context.Missions
.Include(m => m.TeamAWinMission)
.Include(m => m.TeamBWinMission)
.ToList();
return View(result);
}
Reference : https://learn.microsoft.com/en-us/ef/core/querying/related-data

Entity Framework - Relationship confusion

I'm having problems understanding Entity Framework Code Firsts relationship creation, as I'm more used to the traditional way.
The one-to-many relationship seems clear to me: The childs only need a foreignKey ID property indicating to which Parent they belong.
public class Parent
{
public int Id { get; set; }
public virtual ICollection<Child> Childs { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Now, I'm not quite sure how to properly create a many-to-many relationship. Probably, there is an additional table ParentChild necessary, so there is no need for (foreign-key) ID properties right?
public class Parent
{
public int Id { get; set; }
public virtual ICollection<Child> Childs { get; set; }
}
public class Child
{
public int Id { get; set; }
public virtual ICollection<Parent> Parents { get; set; }
}
Now, for the one-to-one relationship, I have no clue.
public class Parent
{
public int Id { get; set; }
public int ChildID { get; set; }
public virtual Child child { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Are the foreign ID properties even needed or can I just have a Child property in the Parent class and a Parent-type property in the Child Class? And is the virtual keyword allowed when I ommit the foreign key ID properties?
I suggest you to have a look in the entity framework fluent api. One to one relation can be easily achieved with the fluent api. Explanation source. For fast reference:
public class Student
{
public Student() { }
public int StudentId { get; set; }
[Required]
public string StudentName { get; set; }
[Required]
public virtual StudentAddress StudentAddress { get; set; }
}
public class StudentAddress
{
[Key, ForeignKey("Student")]
public int StudentId { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public int Zipcode { get; set; }
public string State { get; set; }
public string Country { get; set; }
public virtual Student Student { get; set; }
}
You can override OnModelCreating in your datacontext class.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<StudentAddress>()
.HasKey(e => e.StudentId);
modelBuilder.Entity<StudentAddress>()
.Property(e => e.StudentId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<StudentAddress>()
.HasRequired(e => e.Student)
.WithRequiredDependent(s => s.StudentAddress);
base.OnModelCreating(modelBuilder);
}
With Entity Framework, you don't even have to specify the foreign key relations as it will deduce it from your model and create the tables accordingly. The only relation type you actually have to do something for is the 0..1 or 1 to 0..1 or 1.
Don't forget that the object model is way more permissive than the database model. You can have a property storing a collection in an object but not in a table.
You have to think differently as EF will do the work for you at the databse level and you will have access to all properties defines in your object model, even collection properties.
The rules I always use to get it done is the following :
If the cardinality of the relation is 0..1 or 1, use a reference to the other entity object as your property. If the cardinality is many, use a collection.
Here are some use cases :
1 to Many (many childs per parent) :
public class Parent
{
public int Id { get; set; }
// Navigation property
public virtual ICollection<Child> Childs { get; set; }
}
public class Child
{
public int Id { get; set; }
// Navigation property
public virtual Parent Parent { get; set; }
}
The result in the database would be a Table Parent with a single property (Id) and a table Child with two properties, the Id and the foreign key property automatically generated named Parent_Id (Table name then underscore, then the key property of the related class).
The Many to Many :
public class ClassA
{
public int Id { get; set; }
// Navigation property
public virtual ICollection<ClassB> ClassBs { get; set; }
}
public class ClassB
{
public int Id { get; set; }
// Navigation property
public virtual ICollection<ClassA> ClassAs { get; set; }
}
The result in the database would be a Table ClassA with a single property (Id), a table ClassB with a single property (Id) and a third table (the relation table for the many-to-many relation) with two properties (this Ids of both tables).
EF will deduce what it needs to in order to do the job so you don't have to be more specific then that.
Now for the only somewhat problematic one, the 1 to 1 :
public class ClassA
{
public int Id { get; set; }
// Navigation property
public virtual ClassB ClassB { get; set; }
}
public class ClassB
{
public int Id { get; set; }
// Navigation property
public virtual ClassA ClassA { get; set; }
}
Following the rule I gave at the beginning, this is what we would do. But in this case, EF has no way to know the direction of the relation... A 1-to-1 coule be either direction. We will have to let it know the direction using annotation (to me the easiest way compared to Fluent API).
public class ClassA
{
public int Id { get; set; }
// Navigation property
public virtual ClassB ClassB { get; set; }
}
public class ClassB
{
[ForeignKey("ClassA")]
public int Id { get; set; }
// Navigation property
public virtual ClassA ClassA { get; set; }
}
The annotation [ForeignKey("ClassA")], in ClassB tells EF to use the Id column from ClassB as the foreign key in ClassA.
The result in the database would be a Table ClassA with 2 properties (Id and ClassB_Id) and a table ClassB with a single property (Id).
You don't have to create the foreigh key properties yourself as EF will do it for you.

MVC4 how to load related data without Navigation Properties

I an fairly new to MVC, and have created an MVC4 application using EF-database-first. The database does not contain foreign key definitions and I can't add them (I don't own the database). Here are two example classes from the database:
public partial class Allocation
{
public int AllocID { get; set; }
public int DeptID { get; set; }
public decimal AllocationPercent { get; set; }
}
public partial class Department
{
public int DeptID { get; set; }
public string DeptName { get; set; }
public string Account { get; set; }
}
The default Allocation Index page shows the department ID. I want to show the department name instead. How can I do this without navigation properties?
I tried
public class AllocationController : Controller
{
private Entities db = new Entities();
//
// GET: /Allocation/
public ActionResult Index()
{
return View(db.Allocation.Include(d => d.DeptID).ToList());
}
...
but this gives an error ("A specified Include path is not valid. The EntityType 'TESTModel.Allocation' does not declare a navigation property with the name 'DeptID'.")...
I'm not sure how to code eager-loading or explicit-loading without navigation properties either, which prompted this question. Efficiency-wise, I don't believe it matters which way I load the related information, so any help in any direction would be appreciated.
The database does not have to have definitions, as long as the fields are there and the entities have been placed in the database with referential integrity in mind. All you need to do is let entity framework know about the relationship. This is done with the virtual keyword to create "Navigational Properties".
public partial class Allocation
{
public int AllocID { get; set; }
public int DeptID { get; set; }
public decimal AllocationPercent { get; set; }
public virtual Department Department { get; set; } /* this is your nav property */
}
public partial class Department
{
public int DeptID { get; set; }
public string DeptName { get; set; }
public string Account { get; set; }
}
Now you can do:
db.Allocation.Include(a => a.Department).ToList()
There may be an error which requires you to use a foreign key definition (although I do not think so). If this is the case, you will need to decorate your navigation property like this
[ForeignKey("DeptID")]
public virtual Department Department { get; set; }
You may also try it this way:
public int AllocID { get; set; }
[ForeignKey("Department")]
public int DeptID { get; set; }
public decimal AllocationPercent { get; set; }
public virtual Department Department { get; set; }
With navigation properties, Travis J's answer is what you need.
If you don't want to use navigation properties, assuming your DB context has a set called Departments, you could do smth like this:
var deptId = db.Allocation.DeptID;
var departments = db.Departments.Where(p => p.DeptID == deptId);
return View(departments.ToList());

PetaPoco to return self-referencing hierarchy

How would one write a query/method to return a POCO that is from a self-referencing database as shown in this question
Firstly you would map it a flat class. eg. db.Fetch<CategoryDb>("select * from categories");
public class CategoryDb {
public int Id { get; set; }
public string Name { get; set; }
public int ParentCategoryId { get; set; }
}
From here I would then create a new Object that self referenced itself. (You could use the existing object with the ParentCategory having the [Result] attribute on it.)
public class Category {
public int Id { get; set; }
public string Name { get; set; }
public Category ParentCategory { get; set; }
}
You could then take this and convert your flat list into a nested list.
I do have code somewhere that can do this, and for which it also provides searching methods etc, but its not on this computer. I will update tomorrow with a link to the code.