asp.net mvc 3 EF query from one to many relationship - sql

I am new to asp.net mvc and i have problems that i think i would solve them very easy with asp.net web forms. However the project have to be in mvc, so here is the problem.
I got X tables
table1 Users
int user_ID
string username
table2 Friends
int friendshipID
int user_ID
int friend_ID
In table 2, user_ID represents the current user that is logged in. friend_ID represents ids from his friends. Its one to many relationship.
Now what i want to do, is, in user/details/ID view, show all friends of that user.
The query that i want to make is: first select the friend_IDs from table2 where user_ID=id(from querystring), then select every username from table1 where user_ID = friend_ID.
I think this is really easy in SQL, but dont know how to do it with the mvc syntax.
The controller:
//
// GET: /User/Details/5
public ViewResult Details(int id)
{
User user = db.Users.Find(id);
return View(user);
}
The view:
#model Social2.Models.User
<div class="display-label">Friends</div>
<div class="display-field">
#foreach (var friend in #Model.Friends)
{
#friend.User.username;
}
</div>
The view returns wrong results.
Models
public partial class User
{
public User()
{
this.Albums = new HashSet<Album>();
this.Friends = new HashSet<Friend>();
this.Messages = new HashSet<Message>();
this.Posts = new HashSet<Post>();
this.Groups = new HashSet<Group>();
}
public int user_ID { get; set; }
public System.Guid user_UniqueID { get; set; }
public string username { get; set; }
public virtual ICollection<Album> Albums { get; set; }
public virtual aspnet_Users aspnet_Users { get; set; }
public virtual ICollection<Friend> Friends { get; set; }
public virtual ICollection<Message> Messages { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<Group> Groups { get; set; }
}
and from friends table
public partial class Friend
{
public int friendship_ID { get; set; }
public int user_fr_ID { get; set; }
public int friend_ID { get; set; }
public virtual User User { get; set; }
}
also the context
public partial class ASPNETDBEntities : DbContext
{
public ASPNETDBEntities()
: base("name=ASPNETDBEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Album> Albums { get; set; }
public DbSet<aspnet_Users> aspnet_Users { get; set; }
public DbSet<Friend> Friends { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<Picture> Pictures { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<sysdiagram> sysdiagrams { get; set; }
public DbSet<User> Users { get; set; }
}

As the Friends list property is virtual it will not be included with your query. Try using below query to include the Friends.
public ViewResult Details(int id)
{
User user = db.Users.Include("Friends").FirstOrDefault(u => u.user_ID == id);
//Also for each friend get the User:
foreach (var friend in user.Friends.ToList())
{
friend.User = db.Users.Find(friend.friend_ID);
}
return View(user);
}
View:
<table>
#foreach (var friend in #Model.Friends)
{
<tr>
#Html.DisplayFor(modelItem => friend.User.username)
</tr>
}
</table>
Your model classes doesn't appear to be following the convention for the Entity keys. The fields "user_ID" and "friendship_ID" should be UserId and FriendId. Or if you want to key them like that annotate them with [key] attribute.

Make the ViewModel class of your own. Retrieve the data from database and build the model class object. Pass this model class to view i.e. create your view based on this model class.

Related

EF Core: many-to-many query

I am trying to return all items in UserDTO from a User given a particular Team. The below code only returns the UserId (since that is what contained in TeamUser). Can this be done in a straightforward way? Or do I have to perform a second lookup on Users once the UserId's are given?
public class User
{
public long UserId { get; set; }
public string? Name { get; set; }
[Required]
[EmailAddress]
public string? Email { get; set; } = default!;
[Required]
public string? CreatedDate { get; set; }
/* relationships */
public virtual ICollection<TeamUser>? Teams { get; set; } //many Teams to many Users
}
public class Team
{
public long TeamId { get; set; }
[Required]
public string? Name { get; set; }
[Required]
public string? CreatedDate { get; set; }
/* relationships to User */
public virtual ICollection<TeamUser>? Users { get; set; } //many Users to many Teams
}
public class UserDTO
{
public long UserId { get; set; }
public string? Name { get; set; }
[Required]
[EmailAddress]
public string? Email { get; set; } = default!;
}
public class TeamUser
{
public long TeamId { get; set; }
public Team? Team { get; set; }
public long UserId { get; set; }
public User? User { get; set; }
}
[HttpGet]
public async Task<ActionResult<IEnumerable<UserDTO>>> GetUsers(long? userId, long? teamId)
{
if(teamId == null)
{
return await _context.Users.Select(x => UserToDTO(x)).ToListAsync();
}
else
{
// get users given teamId
return await _context.TeamUsers
.Include(u => u.User)
.Where(t => t.TeamId == teamId)
.Select(pt => new UserDTO
{
UserId = pt.UserId
})
.ToListAsync();
}
EF Core introduced the new ThenInclude() extension method to load multiple levels of related entities.
As EF Core won't load related properties automatically, so you'll need to explicitly do this, but something like the following should do the trick:
var result = context.Begrip
.Include(x => x.Categories)
.ThenInclude(x => x.category);
Note, intellisense doesn't always work on .ThenInclude at the moment, but the code should still compile even if it gets a red underline.
If you're returning this to the view or an API, you'll likely want to map it to a DTO so you don't have to deal with .Categories[0].category.Name etc.

How to configure One to Many relationship in Entity Framework

I am creating API in ASP .NET Core that will retrieve posts with user Id. Post should contain text and Id of a user who posted it.
I have two models Users and Posts and I need help on how to configure this relationship
I want one User to have many posts
Currently my user model contains
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public List<Post> Posts { get; set; }
}
And my Post model
public class Post
{
public int Id { get; set; }
public string Text { get; set; }
}
What is the best way to do this ?
One to many relationships ( User to have many posts).
public class User{
public int Id { get; set; }
public string Username { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Text { get; set; }
//Navigation
public int UserId { get; set; }
public User User{ get; set; }
}
this is your Model Class:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public Virtual List<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Text { get; set; }
public int UserId { get; set; }
public Virtual User User { get; set; }
}
and in your DbContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// configures one-to-many relationship
modelBuilder.Entity<User>().HasMany(x=>x.Posts).WithRequired(x=>x.User)
.HasForeignKey<int>(s => s.UserId);
}

Entity Framework Core one to many bidirectional

Coming from the world of mighty nHibernate trying to get bidirectional relation to work in Entity Framework Core.
Here are my simplified entities:
public class User
{
public int id { get; set; }
public string username { get; set; }
public ICollection<Login> Logins { get; set; }
}
public class Login
{
public int id { get; set; }
public string email { get; set; }
public User User { get; set; }
}
Here is my mapping:
public DbSet<User> Users { get; set; }
public DbSet<Login> Logins { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.ToTable("users")
.HasMany(x=>x.Logins)
.WithOne(x=>x.User)
.HasForeignKey(x=>x.user_id);
modelBuilder.Entity<Login>()
.ToTable("logins")
.HasOne(d=>d.User)
.WithMany(d=>d.Logins)
.HasForeignKey(e => e.user_id);
}
When I query User, Logins collection is fetched correctly. However when I query Login the User comes null.
var login = DBContext.Logins.Where(x => x.email == email).SingleOrDefault();
// login.User is null

ASP.Net MVC 4 SimpleMembership UserID as Foreign Key

Dear SO members please help me to get unstuck in what I thought should be an easy task but I have been stuck in for 2 days. I have a couple of tables that need to have a FK to the UserId of the currently logged in user. Here is an example
UserProfile
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string ManagerName { get; set; }
public string PhoneNumber { get; set; }
}
A customer entity
public class Customer
{
[Key]
public int CustomerId { get; set; }
public string Names { get; set; }
[ForeignKey("UserProfile")]
public int UserId { get; set; }
public virtual UserProfile UserProfile { get; set; }
}
The DB Context
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Customer> Customers { get; set; }
}
Customer Controller
[HttpPost]
[InitializeSimpleMembership]
[ValidateAntiForgeryToken]
public ActionResult Create(Customer model)
{
if (ModelState.IsValid)
{
model.UserProfile = db.UserProfiles.FirstOrDefault(u => u.UserId == WebSecurity.CurrentUserId);
db.Customers.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UserId = new SelectList(db.UserProfiles, "UserId", "UserName", model.UserId);
return View(model);
}
Also tried
[HttpPost]
[InitializeSimpleMembership]
[ValidateAntiForgeryToken]
public ActionResult Create(Customer model)
{
if (ModelState.IsValid)
{
model.UserId = WebSecurity.GetUserId(User.Identity.Name);
db.Customers.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.UserId = new SelectList(db.UserProfiles, "UserId", "UserName", model.UserId);
return View(model);
}
In each case I get the error message
"The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Customers_dbo.UserProfile_UserId". The conflict occurred in database "ValueCardProject", table "dbo.UserProfile", column 'UserId'.
The statement has been terminated."
All I want to do is to be able to create tables with a FK to the UserID of the logged in user. For the interest of full disclosure, Customers should not be created directly as users, in my use case the current logged in users is a Retailer, so what I am trying to accomplish is to create a customer record with the FK to RetailerId (which is the UserId in the UserProfile).
Thanks for your time.
I believe your foreign key attribute on UserId is in the wrong place.
you should be doing this instead:
public int UserId { get; set; }
[ForeignKey("UserId")]
public virtual UserProfile UserProfile { get; set; }

CodeFirst - Update single property

We are using EF5, Code First approach to an MVC4 app that we're building. We are trying to update 1 property on an entity but keep getting errors. Here's what the class looks like which the context created:
public partial class Room
{
public Room()
{
this.Address = new HashSet<Address>();
}
public int RoomID { get; set; }
public Nullable<int> AddressID { get; set; }
public Nullable<int> ProductVersionID { get; set; }
public string PhoneNumber { get; set; }
public string AltPhone { get; set; }
public string RoomName { get; set; }
public string Description { get; set; }
public string Comments { get; set; }
public string Notes { get; set; }
public virtual ICollection<Address> Address { get; set; }
}
Here's our ViewModel for the view:
public class RoomDetailsViewModel
{
//public int RoomID { get; set; }
public string RoomName { get; set; }
public string PhoneNumber { get; set; }
public string AltPhone { get; set; }
public string Notes { get; set; }
public string StateCode { get; set; }
public string CountryName { get; set; }
public string ProductVersion { get; set; }
public int PVersionID { get; set; }
public List<SelectListItem> ProductVersions { get; set; }
public Room Room { get; set; }
}
Here's the Controller Action being called on "Save":
[HttpPost]
public virtual ActionResult UpdateRoom(RoomDetailsViewModel model)
{
var db = new DBContext();
bool b = ModelState.IsValid;
var rooms = db.Rooms;
var rm = rooms.Where(r => r.RoomID == model.Room.RoomID).Single();
//List<Address> address = db.Addresses.Where(a => a.AddressID == rm.AddressID).ToList<Address>();
rm.ProductVersionID = model.PVersionID;
//rm.Address = address;
db.Entry(rm).Property(r => r.ProductVersionID).IsModified = true;
//db.Entry(rm).State = System.Data.EntityState.Modified;
db.SaveChanges();
return View("RoomSaved", model);
}
All this view does is display data and allow the user to change the Product Version (from a SelectList), so, in the Room Entity, all we are updating is the ProductVersionID property, nothing else. We can get the data to display properly but when we click "save", we get this error:
An object of type 'System.Collections.Generic.List`1[[Models.Address,
Web.Mobile.TestSite, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]]' cannot be set or removed from the Value
property of an EntityReference of type 'Models.Address'.
As you can see by the Controller Action, we've tried several different things but all seem to produce this error. I've tried to populate the model.Room.Address collection with an Address, without, but still get this error.
I read this StackOverflow article and this article as well but neither have solved my problem.
ANY help with this would be greatly appreciated!
After hours and hours of digging, turns out that EF did not import some of the PK's for my DB tables. What tipped me off to this was on the Room class, the PK RoomID did not have the [Key] attribute on it. I tried to reimport the table through the edmx but it never came through as a key (even though it's clearly marked PK in the DB). So, to get around it, I created a partial class of my DBContext and override the OnModelCreating event and included the key, like so:
public partial class DBContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Models.Room>().HasEntitySetName("Rooms");
modelBuilder.Entity<Models.Room>().HasKey(r => r.RoomID);
}
}
Once this was done, the Action saved the record as hoped.
I hope this helps someone else!