EntityFramework and relationtionships - asp.net-mvc

Imagine the person Ted. Ted use bulletin board. He can post ads and subscribe to ads (imagine ad is some event.. teaching lessons for example). So as you guessed we have two objects: Advert and User (Ted). One user posts different adverts and in the other hand one user subscribes for different adverts. So we have two types of relationships: One-to-Many (Ted to his adverts) and Many-to-Many (Advert to subscribers - subscriber to adverts).
So the Models looks like:
[Table("Users")]
public class User
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Display(Name="Login")]
[Required(ErrorMessage = "Enter the login.")]
public string Name { get; set; }
[Required(ErrorMessage = "Select the city.")]
public int CityId { get; set; }
public City City { get; set; }
public int? SpecialityId { get; set; }
public Speciality Speciality { get; set; }
public virtual ICollection<Advert> Adverts { get; set; }
public virtual ICollection<Advert> SubscribedOnAdverts { get; set; }
public User()
{
Adverts = new List<Advert>();
SubscribedOnAdverts = new List<Advert>();
}
}
And Advert:
public class Advert
{
public int Id { get; set; }
[Required(ErrorMessage = "Enter your ad title")]
public string Title { get; set; }
[Required(ErrorMessage = "Enter your ad text")]
[Display(Name="Advert text")]
public string Description { get; set; }
public int? Price { get; set; }
[DisplayName("Owner")]
[Required(ErrorMessage = "Select ad owner")]
public int UserId { get; set; }
public User User { get; set; }
[DisplayName("Speciality")]
[Required(ErrorMessage = "Enter the speciality")]
public int SpecialityId { get; set; }
public Speciality Speciality { get; set; }
[Column(TypeName = "DateTime2")]
public DateTime publishTime { get; set; }
public virtual ICollection<User> SubscribedUsers { get; set; }
public Advert() {
publishTime = DateTime.Now;
SubscribedUsers = new List<User>();
}
}
Let's look at the User model: you can see the same type of props: Adverts, SubscripedOnAdverts. If we delete SubscribedOnAdverts property from the User model and SubscribedUsers from the Advert model, following code will perfom correct:
IEnumerable<Advert> userAdverts = db.Users.Where(usr => usr.Id == id).FirstOrDefault().Adverts;
It will return adverts this user posted.
But with the above properies (SubscribedOnAdverts in User model, SubscribedUsers in Advert model) it will return empty Collection.
Normal:
With problem:
I can guess why it happens. But i don't know how to fix this issue and save the relationships.

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();

I have class Courses attached with 3 classes I want to display studydetails by selecting department & session lists

public class Courses
{
[Key]
public int Course_ID { get; set; }
[Display(Name = "Department")]
public Department Departments { get; set; }
public int Department_ID { get; set; }
[Display(Name = "Semester")]
public Semester Semesters { get; set; }
public int SemesterID { get; set; }
[Display(Name = "Session")]
public SessionDetail SessionDetails { get; set; }
public int SessionID { get; set; }
[Display(Name = "Course Title")]
public string Course_Title { get; set; }
[Display(Name = "Course Code")]
public string Course_Code { get; set; }
[Display(Name ="Credit Hours")]
public string Cridth_Hours { get; set; }
}
This is my Class named Courses that is attatched with 3 other classes named Departments SessionDetail and Semester. I want that a view shows me the course details of a specific session and department of students form 2 dropdown lists of department and session.

Join two Models to display in webpage

I'm building an application using ASP.NET MVC4 with code first data migrations. I have an estimates model, a clients model, a DbContext, and a view model I created. I am wanting to display the company name in a drop down, with the company name tied to an estimate. I have a ClientId in both models. I also created a DbSet<> and that didn't work either when querying against it.
I tried to create a viewmodel that I thought I could simply query against and display through my controller. I'm not having any luck in getting this to work. After a day plus of looking on here and other places, I'm out of ideas.
How can I query/join the two models, or query the viewmodel to get the company name associated with the clientId? Thanks for the help.
Models:
public class Estimates
{
[Key]
public int EstimateId { get; set; }
public int ClientId { get; set; }
public decimal EstimateAmount { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime EstimateDate { get; set; }
public string EstimateNotes { get; set; }
}
public class Clients
{
[Key]
public int ClientId { get; set; }
public string CompanyName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public ICollection<Estimates> Estimates { get; set; }
public ICollection<Contracts> Contracts { get; set; }
}
public class ClientEstimateViewModel
{
public Clients Clients { get; set; }
public Estimates Estimates { get; set; }
}
public class NovaDb : DbContext
{
public NovaDb(): base("DefaultConnection")
{
}
public DbSet<Clients> Clients { get; set; }
public DbSet<Estimates> Estimates { get; set; }
public DbSet<Contracts> Contracts { get; set; }
public DbSet<Invoices> Invoices { get; set; }
public DbSet<ClientEstimateViewModel> ClientViewModels { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Controller:
NovaDb _db = new NovaDb();
ClientEstimateViewModel ce = new ClientEstimateViewModel();
public ActionResult Index()
{
var model =
(from r in ce.Clients
join x in ce.Estimates
where
//var model =
// from r in _db.Clients
// orderby r.CompanyName ascending
// select r;
return View(model);
}
Because you've created the relationship between client & estimate in your models, you should be able to create a query like this:
var query = from c in _db.clients
select new ClientEstimateViewModel
{
Clients = c,
Estimates = c.Estimates
}
Although you'd have to change your model so Estimates was public List<Estimates> Estimates { get; set; }
This would give you a collection of ClientEstimateViewModel which you could then pass to your view

MVC 5 Razor code-first EF Junction Table with IdentityUser

So I've got these 2 class and User coming from individual user accounts
public class User : IdentityUser
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Email { get; set; }
public virtual ICollection<Basket> Baskets { get; set; }
}
public class Basket
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Key]
public int BasketId { get; set; }
public string Name { get; set; }
public int Words { get; set; }
public virtual ICollection<User> Users { get; set; }
}
EF has created UserBaskets with 2 FK. I have items in my Basket class that I seeded.
My question is, how can I add row to my junction table in the controller? For example, a logged user click on a basket and return the Id...Now I've got
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Basket(int? basketid)
{
if (ModelState.IsValid)
{
var job = User.Identity.GetUserId();
job.Baskets.Add(basketid);
db.Users.Add(job);
db.SaveChanges();
return RedirectToAction("Basket");
}
return View(db.Baskets.ToList());
}
Thank you for any help.
I finally chose to add a junction table manually
public class UserBasket
{
[Key]
public int UserBasketId { get; set; }
public virtual User User { get; set; }
public virtual Basket Basket { get; set; }
public DateTime Date { get; set; }
}
and could add a row using identityUser like this
UserManager<User> userManager = new UserManager<User>(new UserStore<User>(db));
var user = userManager.FindById(User.Identity.GetUserId());
Basket basket = db.Basket.Find(id);
var userbasket = new UserBasket {User = user, Basket = basket, Date = DateTime.Now };
db.UserBasket.Add(userbasket);
db.SaveChanges();

Pass model to controller and send result to page not using form submit

I have a page I am writing to send emails to customers. One of the ways to select customers to email is to select products they have. Each product has different detail information and you will also be able to choose specifics about the product to narrow down who you are emailing.
Because this is going to be complex I need to do the processing to come up with the customer email list on the controller side but it would be a nightmare to try to pull all the data form the controls to send to it manually.
I would like to use an AJAX call that on the controller side would get the model tied to the view, query the database, and send back the list of emails so on the view I can pop up outlook for them with the list of email addresses already populated. Because I need to go back to the view with data I don't think I can do this with a form post which is how I normally get model into into the controller.
Does someone know how I could accomplish this?
Here are some of the clases I have to try to help people understand my layout
public class ProductType
{
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[Required(ErrorMessage = "Please enter a product type description")]
public string Description { get; set; }
public virtual ICollection<ProductTypeDetail> ProductDetails { get; set; }
}
public class ProductTypeDetail
{
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[HiddenInput(DisplayValue = false)]
public int? ProductTypeID { get; set; }
public ProductType ProductType { get; set; }
[Required(ErrorMessage = "Please enter a description")]
public string Description { get; set; }
[Required(ErrorMessage = "Please enter a valid type")]
public string Type { get; set; }
public virtual ICollection<ProductTypeDetailValidValue> ValidValues { get; set; }
}
The above 2 classes are for product types which someone could enter anything for and as many as they want. The product details are detail information you might need to know about your products. For example you could have a product type of Vehicle Registration System and put a product detail item in that for a specific import process that pertains to the product they you need to know if they use or not.
public Customer()
{
SiteVisits = new List<SiteVisit>();
Payments = new List<Payment>();
Contacts = new List<CustomerEmail>();
}
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[Display(Name = "Name")]
[Required(ErrorMessage = "Please enter a customer name")]
public string CustomerName { get; set; }
[Display(Name = "Line 1")]
public string Address1 { get; set; }
[Display(Name = "Line 2")]
public string Address2 { get; set; }
[Display(Name = "Line 3")]
public string Address3 { get; set; }
[Display(Name = "Line 4")]
public string Address4 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
[HiddenInput(DisplayValue = false)]
[Required(ErrorMessage = "Please enter a customer type")]
public int CustomerTypeID { get; set; }
[Display(Name = "Type")]
public virtual CustomerType CustomerType { get; set; }
[HiddenInput(DisplayValue = false)]
[Required(ErrorMessage = "Please enter a customer status")]
public int CustomerStatusID { get; set; }
[Display(Name = "Status")]
public virtual CustomerStatus CustomerStatus { get; set; }
[DataType(DataType.MultilineText)]
public string Comments { get; set; }
[Required(ErrorMessage = "Please enter an expiration year")]
public long ExpirationYear { get; set; }
[Required(ErrorMessage = "Please enter an expiration month")]
public long ExpirationMonth { get; set; }
[Required(ErrorMessage = "Please enter a control name")]
public string ControlName { get; set; }
public Boolean Networked { get; set; }
public long Population { get; set; }
[Display(Name = "First Fiscal Month")]
public long FirstMonth { get; set; }
[Display(Name = "FTP User Name")]
public string FTPUserName { get; set; }
[Display(Name = "FTP Password")]
public string FTPPassword { get; set; }
[Display(Name = "Customer ID")]
public string CustomerUpdateID { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Customer Since")]
public DateTime? StartDate { get; set; }
public virtual ICollection<CustomerPhoneNumber> PhoneNumbers { get; set; }
public virtual ICollection<CustomerProduct> Products { get; set; }
public virtual ICollection<CustomerEmail> Contacts { get; set; }
public virtual ICollection<SiteVisit> SiteVisits { get; set; }
public virtual ICollection<Payment> Payments { get; set; }
}
public class CustomerProduct
{
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[HiddenInput(DisplayValue = false)]
public int? ProductTypeID { get; set; }
public virtual ProductType ProductType { get; set; }
[HiddenInput(DisplayValue = false)]
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
[HiddenInput(DisplayValue = false)]
public int? VersionID { get; set; }
public virtual ProductVersion Version { get; set; }
[HiddenInput(DisplayValue = false)]
public int? StatusID { get; set; }
public virtual ProductStatus Status { get; set; }
public virtual ICollection<CustomerProductDetail> ProductDetails { get; set; }
}
public class CustomerProductDetail
{
[HiddenInput(DisplayValue = false)]
public int ID { get; set; }
[HiddenInput(DisplayValue = false)]
public int CustomerProductID { get; set; }
public virtual CustomerProduct CustomerProduct { get; set; }
[HiddenInput(DisplayValue = false)]
public int ProductTypeDetailID { get; set; }
public virtual ProductTypeDetail ProductTypeDetail { get; set; }
//[Required(ErrorMessage = "Please enter a value")]
public string Value { get; set; }
}
So above I have the customer class. Each customer can be set up with any number of the product types you have set up and you can select values for the product details of that product type for this particular customer. The customers also contain contacts. that is the class that has the email addresses.
So I need to show a screen that displays all the product types you have set up and lets you select values for detail items on products you select then I need to query and find customers that match this
All I needed to do was serialize the form and pass it as data in my ajax call. If on the Controller side that is getting called I have an argument that is of the same type as the model my view is strongly typed to the model binder is smart enough to fill in my object automatically

Resources