ASP.NET MVC One-to-One Relationship - asp.net-mvc

After i finished my SimpleMembership Step on my Application, there is another problem...
namespace Korbball.Models
{
public class GamesContext : DbContext
{
public GamesContext()
: base("KorbballDBContext")
{
}
public DbSet<Games> Games { get; set; }
public DbSet<Teams> Teams { get; set; }
}
public class Games
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int GameId { get; set; }
public int TeamA { get; set; }
public int TeamB { get; set; }
public int ResultA { get; set; }
public int ResultB { get; set; }
public int League { get; set; }
public DateTime StartDate { get; set; }
public Boolean Finished { get; set; }
}
public class Teams
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int TeamId { get; set; }
public string TeamName { get; set; }
}
}
My target is, to create Games with Results etc.
The TeamA & TeamB column shoukd be the TeamID from the Teams Table.
Whats steps i have to do to set the correct relationship.
Additional Informations:
Games Table ->
GamesID = 1
TeamA = 1
TeamB = 2
ResultA = 10
ResultB = 8
Teams Table ->
TeamId = 1
TeamName = "Manchester"
TeamId = 2
TeamName = "Zurich"
On the view ->
Manchester 10 : 8 Zurich

I'm not sure if this is what you want to do, but if you create it like this, with the public virtual Team, the entity framework will automatically take care of the mapping for you and you can just access these "navigation properties" like normal objects.
namespace Korbball.Models
{
public class GamesContext : DbContext
{
public GamesContext()
: base("KorbballDBContext")
{
}
public DbSet<Games> Games { get; set; }
public DbSet<Teams> Teams { get; set; }
}
public class Games
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int GameId { get; set; }
public virtual Team TeamA { get; set; }
public virtual Team TeamB { get; set; }
public int ResultA { get; set; }
public int ResultB { get; set; }
public int League { get; set; }
public DateTime StartDate { get; set; }
public Boolean Finished { get; set; }
}
public class Teams
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int TeamId { get; set; }
public string TeamName { get; set; }
}
}
EDIT:
Ok, this is how I usually do this:
I create virtual fields for all navigation properties, which in your case would only be the Teams as I already pointed out.
Then, when you create the Teams (which have to be there before the game can exist), you just add them like:
var teamM = new Team(){TeamName = "Manchester"};
var teamZ = new Team(){TeamName = "Zurich"};
// 'db' is your DbContext
db.Teams.Add(teamM);
db.Teams.Add(teamZ);
// This could also happen through some user form or something.
db.SaveChanges();
// this is important, because only after you've safed entities to you db,
// the [DatabaseGenerated] key will be set.
var game1 = new Game();
game1.ResultA = 10;
game1.ResultB = 8;
//etc.
game1.TeamA = teamM;
game1.TeamB = teamZ;
// we still have those from up when we created an db.SaveChanges'd them
// now save everything and the Entity Framework will take care of all the relationships
db.SaveChanges();
If you have already saved the Teams and they exist in you database, you can do something like:
var team1 = db.Teams.Find(0); // 0 beeing the [Key] => Manchester
// or
var team2 = db.Teams.Where(m => m.Name.Contains("Zu"); // => Zurich
// Now do the same thing with game1:
game1.TeamA = team1;
game1.TeamB = team2;
db.SaveChanges();
I hope this is what you want to know.
If this won't work, you need to take a look at some up-to-date tutorials about entity framework code first. Lots of things can go wrong with all the initialization / database creation.

Ok, i changed the code into this:
public class GamesContext : DbContext
{
public GamesContext()
: base("KorbballDBContext")
{
}
public DbSet<Games> Games { get; set; }
public DbSet<Teams> Teams { get; set; }
}
public class Games
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int GameId { get; set; }
public virtual Teams TeamA { get; set; }
public virtual Teams TeamB { get; set; }
public int ResultA { get; set; }
public int ResultB { get; set; }
public int League { get; set; }
public DateTime StartDate { get; set; }
public Boolean Finished { get; set; }
}
public class Teams
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int TeamId { get; set; }
public string TeamName { get; set; }
}
My Controller looks like this:
public class GamesController : Controller
{
GamesContext db = new GamesContext();
//
// GET: /Games/
public ActionResult Index()
{
var games = from game in db.Games
join teama in db.Teams on game.TeamA.TeamId equals teama.TeamId
join teamb in db.Teams on game.TeamB.TeamId equals teamb.TeamId
select game;
return View(games.ToList());
}
}
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.TeamA.TeamName)
</td>
<td>
#Html.DisplayFor(modelItem => item.ResultA)
</td>
<td>
#Html.DisplayFor(modelItem => item.ResultB)
</td>
<td>
#Html.DisplayFor(modelItem => item.TeamB.TeamName)
</td>
</tr>
}

Related

How to retrieve object from database using LINQ expression from many-to-many join table

I want to retrieve all players from db for one club where they are in M:M relationship using PlayerClubs join table. My code is working but really doesn't want that approach for example, first I am retrieving all players from db
var players = await _context.PlayerClubs.Where(pc => pc.ClubId == id).Select(p => p.Player).ToListAsync();
then I retrieve the club based on id which I receive from controller
var club = await _context.Clubs.Where(z => z.Id == id).FirstOrDefaultAsync();
and lastly populate ClubViewModel with this data
return new ClubViewModel()
{
Players = players,
Club = club,
};
Now I want to populate this new ClubViewModel with just one db call i.e. one query using linq expression.
Things that I have tried
var query = (from c in _context.Clubs
join pc in _context.PlayerClubs on c.Id equals pc.ClubId
join player in _context.Players on pc.PlayerId equals player.Id
where c.Id == id
select new ClubViewModel
{
Players = player,
Club = c,
}).ToListAsync();
but I got stuck.
PlayerClubs table
Club
public class Club
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[EnumDataType(typeof(Gender))]
public Gender GenderType { get; set; }
public int SeasonId { get; set; }
public virtual Season Season { get; set; }
[Required]
public string YearOfEstablishment { get; set; }
[Required]
public string YearOfEntryIntoLeague { get; set; }
public string Note { get; set; }
[ForeignKey("League")]
public int LeagueId { get; set; }
public virtual League League { get; set; }
public virtual ICollection<PlayerClub> PlayerClubs { get; set; }
public virtual ICollection<CoachClub> CoachClubs { get; set; }
}
Player
public class Player
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string FullName { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime Birth { get; set; }
public int LicenseNumber { get; set; }
public string Note { get; set; }
public virtual List<string> Clubs { get; set; }
public virtual List<Club> Klubovi { get; set; }
public virtual List<string> ClubNames { get; set; }
[StringLength(13, ErrorMessage = "Матичниот број не може да биде подолг од 13 цифри")]
public string Embg { get; set; }
public virtual ICollection<PlayerClub> PlayerClubs { get; set; }
public Player()
{
Clubs = new List<string>();
ClubNames = new List<string>();
Klubovi = new List<Club>();
}
}
PlayerClub
public class PlayerClub
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("Club")]
public int ClubId { get; set; }
[ForeignKey("Player")]
public int PlayerId { get; set; }
public virtual Club Club { get; set; }
public virtual Player Player { get; set; }
}
ClubViewModel
public class ClubViewModel : Club
{
public Club Club { get; set; }
public List<Player> Players { get; set; }
public ClubViewModel()
{
Players = new List<Player>();
}
}
Your query:
var query =
from c in _context.Clubs
where c.Id == id
select new ClubViewModel
{
Players = c.PlayerClubs.Select(pc => pc.Player).ToList(),
Club = c,
};
var result = await query.ToListAsync();

ASP.Net MVC and using Linq to Entities

I'm following an MVC 5 tutorial: http://www.c-sharpcorner.com/article/dashboard-application-with-asp-net-mvc-5-and-jquery/
but the Author left out 1 of the features - the middle panel - "Orders" (which is the orders for all customers). It has a View Details link but no code and partial view is displayed when clicked.
So I'm trying to do create that partial view but having trouble with writing the Linq To Entities.
I am trying for just a partial view that is like a header/multi detail:
CustomerName CustomerImage
OrderDate
Quantity ProductType, ProductName, ProductImage
OrderDate
Quantity ProductType, ProductName, ProductImage
CustomerName CustomerImage
OrderDate
Quantity ProductType, ProductName, ProductImage
Here is the ViewModels I created to represent the above:
public class OrderDetailsViewModel
{
public int Quantity { get; set; }
public string ProductType { get; set; }
public string ProductName { get; set; }
public string ProductImage { get; set; }
}
public class CustomerOrdersViewModel
{
public string CustomerName { get; set; }
public string CustomerImage { get; set; }
public DateTime OrderDate { get; set; }
public ICollection<OrderDetailsViewModel> OrderDetailsViewModel{ get;
set; }
}
Here is the DbContext and the models that the Author created:
DbContext:
public class DashboardContext : DbContext
{
// Constructor - inherits the base constructor.
public DashboardContext() : base("DashboardOrder")
{
}
public IDbSet<Customer> CustomerSet { get; set; }
public IDbSet<Order> OrderSet { get; set; }
public IDbSet<Product> ProductSet { get; set; }
public IDbSet<OrderDetails> OrderDetailSet { get; set; }
}
Customer:
public class Customer : IEntity
{
public Customer()
{
Orders = new List<Order>();
}
public int ID { get; set; }
public string CustomerName { get; set; }
public string CustomerEmail { get; set; }
public string CustomerPhone { get; set; }
public string CustomerCountry { get; set; }
public string CustomerImage { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
IEntity:
public interface IEntity
{
int ID { get; set; }
}
Order:
public class Order : IEntity
{
public Order()
{
OrderDetail = new List<OrderDetails>();
}
public int ID { get; set; }
public DateTime OrderDate { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<OrderDetails> OrderDetail { get; set; }
}
OrderDetails:
public class OrderDetails : IEntity
{
public int ID { get; set; }
public int Quatity { get; set; }
public virtual Order Order { get; set; }
public virtual Product Product { get; set; }
}
Product:
public class Product : IEntity
{
public Product()
{
OrderDetails = new List<OrderDetails>();
}
// Auto-implemented properties.
public int ID { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public int UnitsInStock { get; set; }
public string ProductImage { get; set; }
public string ProductType { get; set; }
public virtual ICollection<OrderDetails> OrderDetails { get; set; }
}
Here is the Action Method in the DashboardController I was attempting to write.
I believe I want to read the Order Model which in turn has reference to the Customer and a list of Orders which in turn has a reference to the list of OrderDetail which has reference to the Product.
But I'm having a hard time with the "Linq to SQL" to get the data in the lists in the models to create my ViewModel to flatten it out and pass to my partial view as a list.
public ActionResult GetCustomerOrdersDetails()
{
List<CustomerOrdersViewModel> customerOrders = null;
using (DashboardContext _context = new DashboardContext())
{
// Using LINQ TO SQL and deferred execution via the "ToList".
customerOrders = (from o in _context.OrderSet
select new CustomerOrdersViewModel
{
CustomerName = o.Customer.CustomerName,
CustomerImage = o.Customer.CustomerImage,
OrderDate = o.OrderDate,
-- Here I need to process the list of Orders which in turn has a reference to the list of OrderDetail which has
reference to the Product.
ProductType = ?,
ProductName = ?,
ProductImage = ?,
Quantity = ?,
}).ToList();
}
return PartialView("~/Views/Dashboard/GetCustomerOrdersDetails.cshtml", customerOrders);
}
You first need a .GroupBy() clause to group the records by CustomerName, CustomerImage and OrderDate. Then because OrderDetail is a collection proeprty, you need a .SelectMany() to 'flatten that collection before projecting the result to your OrderDetailsViewModel model.
List<CustomerOrdersViewModel> model = db.OrderSet
.GroupBy(x => new { Name = x.Customer.CustomerName, Image = x.Customer.CustomerImage, Date = x.Date })
.Select(x => new CustomerOrdersViewModel
{
CustomerName = x.Key.Name,
CustomerImage = x.Key.Image,
OrderDate = x.Key.Date,
OrderDetailsViewModel = x.SelectMany(y => y.OrderDetail).Select(y => new OrderDetailsViewModel
{
ProductName = y.Product.ProductName,
Quantity = y.Quantity,
ProductType = y.Product.ProductType,
ProductImage = y.Product.ProductImage
}).ToList()
}).ToList();
return PartialView(model);
Then in the view you can use nested loops to display the details of each order
#model IEnumerable<CustomerOrdersViewModel>
....
#foreach(var order in Model)
{
.... // display details of customer name, date etc
#order.CustomerName
foreach(var item in order.OrderDetailsViewModel)
{
.... // display details of product, quantity etc for each order
#item.ProductName

Using LINQ for Inner Join() method

I have two tables:
public class Game
{
public int GameId { get; set; }
public int FirstTeamId { get; set; }
public int GoalsFirstTeam { get; set; }
public int GoalsSecondTeam { get; set; }
public int SecondTeamId { get; set; }
public Team Team { get; set; }
}
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<Game> Games { get; set; }
}
Then, using:
OperationDataContext dt = new OperationDataContext();
public ActionResult Scores()
{
List<Games> model = dt.Games.ToList();
var query = (from g in dt.Games
join t1 in dt.Teams on g.FirstTeamId equals t1.Id
join t2 in dt.Teams on g.SecondTeamId equals t2.Id
select new Games(){g.GameId,
t1.Name,
g.GoalsFirstTeam,
g.GoalsSecondTeam,
t2.Name
}
).ToList();
model = query;
return View(model);
What I am trying to ultimately achieve in View is this:
Game FirstTeam Goals Goals SecondTeam
1 Roma 1 3 Manchester
2 Inter 0 0 Milan
Can somebody help me with this, because I'm getting error notifications ?
PS. When I try this:
OperationDataContext dt = new OperationDataContext();
public ActionResult Scores()
{
var games = dt.Games.ToList();
return View(games);
}
The result is:
Game FirstTeam Goals Goals SecondTeam
1 Roma 1 3 Roma
2 Inter 0 0 Inter
Change:
select new Games(){ g.GameId,
t1.Name,
g.GoalsFirstTeam,
g.GoalsSecondTeam,
t2.Name
}
To an anonymous type:
select new { GameId = g.GameId,
Team1 = t1.Name,
Team2 = t2.Name,
Team1Goals = g.GoalsFirstTeam,
Team2Goals = g.GoalsSecondTeam
}
You can also define a new type - GameResult that consists the fields above.
As Erik suggested you can improve your model
public class Game
{
public int Id { get; set; }
public int FirstTeamId { get; set; }
public virtual Team FirstTeam { get; set; }
public int GoalsFirstTeam { get; set; }
public int GoalsSecondTeam { get; set; }
public int SecondTeamId { get; set; }
public virtual Team SecondTeam { get; set; }
//What does this mean??
public Team Team { get; set; }
}
public class Team
{
public int Id { get; set; }
public string Name { get; set;}
}
Then you can query your data this way
OperationDataContext dt = new OperationDataContext();
public ActionResult Scores()
{
var query = dt.Games.Select(g=> new
{
Game = g.Id,
FirstTeam = g.FirstTeam.Name,
Goals_FT = g.GoalsFirstTeam,
Goals_ST = g.GoalsSecondTeam,
SecondTeam = g.SecondTeam.Name
}
return View(query.ToList());
}
Hope it is clear and helpful,
Alessandro

Populating navigation properties of navigation properties

How do I populate a navigation property with specific value?
I have 3 models, Game, UserTeam, User, defined below. I have a razor view which uses the model IEnumerable. This view loops over the Games, and within that loop, loops over the UserTeams. So far, so good.
Within the UserTeam loop, I want to access the User properties, but they are null. How do I populate the User navigation property for each UserTeam object? Do I need a constructor with a parameter in the UserTeam model?
Models
public class Game
{
public Game()
{
UserTeams = new HashSet<UserTeam>();
}
public int Id { get; set; }
public int CreatorId { get; set; }
public string Name { get; set; }
public int CurrentOrderPosition { get; set; }
public virtual UserProfile Creator { get; set; }
public virtual ICollection<UserTeam> UserTeams { get; set; }
}
public class UserTeam
{
public UserTeam()
{
User = new UserProfile();
}
public int Id { get; set; }
public int UserId { get; set; }
public int GameId { get; set; }
public int OrderPosition { get; set; }
public virtual UserProfile User { get; set; }
public virtual Game Game { get; set; }
public virtual IList<UserTeam_Player> UserTeam_Players { get; set; }
}
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string test { get; set; }
public UserProfile()
{
UserTeams = new HashSet<UserTeam>();
}
public virtual ICollection<UserTeam> UserTeams { get; set; }
[ForeignKey("CreatorId")]
public virtual ICollection<Game> Games { get; set; }
}
Loop in my Razor view (Model is IEnumerable)
#foreach (var item in Model) {
#foreach (var userteam in item.UserTeams) {
#Html.ActionLink("Join game as"+userteam.User.UserName, "JoinGame", new { gameid = item.Id, userid=userteam.UserId })
}
}
Method in my repository that returns the Games
public IEnumerable<Game> GetAllGames()
{
using (DataContext)
{
var gm = DataContext.Games.Include("UserTeams").ToList();
return gm;
}
}
You would need to include this in your repository method. If you are using eager loading then it would be something like
var gm = DataContext.Games
.Include(x => x.UserTeams)
.Include(x => x.UserTeams.Select(y => y.User))
.ToList();
I have not done this without using LINQ for my queries, but I assume it would be something like:
var gm = DataContext.Games.Include("UserTeams.User").ToList();
Hopefully this helps you out

Code first foreign keys

Within my view, I need to return FullName for every Initiator and Owner. To do so, should both Initiator and Owner be foreign keys of EmployeeID?
I'm not sure how to make both Initiator and Owner foreign keys using code first nor how to display FullName using razor.
Any ideas? Thanks.
ChangeRequest
namespace Project.Models
{
public class ChangeRequest
{
public int ChangeRequestID { get; set; }
public int Initiator { get; set; }
public int Owner { get; set; }
}
}
Employee
namespace Project.Models
{
public class Employee
{
public int EmployeeID { get; set; }
public string Surname { get; set; }
public string FirstName { get; set; }
public string FullName
{
get
{
return Surname + ", " + FirstName;
}
}
}
}
View
#foreach (var item in Model.ChangeRequests)
{
#Html.DisplayFor(modelItem => item.Initiator)
#Html.DisplayFor(modelItem => item.Owner)
}
You haven't mentioned EF version, if you want lazy loading, or if you are using attributes or Fluent api, so here's a good reference for Many-Valued Assocations using Fluent API and one using DataAnnotations, and a bit of example code making some assumptions about version, lazy loading and attributes:
public class ChangeRequest
{
[Key]
public int ChangeRequestID { get; set; }
public int? InitiatorId { get; set; }
public int? OwnerId { get; set; }
[ForeignKey("InitiatorId")]
public virtual Employee Initiator { get; set; }
[ForeignKey("OwnerId")]
public virtual Employee Owner { get; set; }
}
public class Employee
{
[Key]
public int EmployeeID { get; set; }
public string Surname { get; set; }
public string FirstName { get; set; }
[InverseProperty("Initiator")]
public virtual ICollection<ChangeRequest> CrsInitiated { get; set; }
[InverseProperty("Owner")]
public virtual ICollection<ChangeRequest> CrsOwned { get; set; }
[NotMapped]
public string FullName
{
get
{
return Surname + ", " + FirstName;
}
}
}
Razor:
#foreach (var item in Model.ChangeRequests)
{
#Html.DisplayFor(modelItem => item.Initiator.FullName)
#Html.DisplayFor(modelItem => item.Owner.FullName)
}

Resources