Code first EF 6 auto generated relation table with additional column - asp.net-mvc

I am trying to create new table with relation many to many in the code first.
I have got two tables: Users and Projects. Now, i want to have relation many to many for this two tables. So i do following:
public class Project
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string PositionCount { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class User : IdentityUser<Guid, UserLogin, UserRole, UserClaim>
{
public override Guid Id { get; set; }
public string Tags { get; set; }
public User()
{
Id = Guid.NewGuid();
}
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User, Guid> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public virtual ICollection<Project> Projects { get; set; }
}
public class RecruitmentDbContext : IdentityDbContext<User, MyRole, Guid, UserLogin, UserRole, UserClaim>
{
public DbSet<Project> Projects { get; set; }
public DbSet<UserProject> UserProjects { get;set; }
public RecruitmentDbContext()
: base("RecruitmentDB")
{
}}
I saw on this page that this is enough to create new table with relation many to many. This is true, because everything works properly. But i want to add new column to this auto generated table, with name "ApplicationStatus". I need this column, because i need to know, if for current user, current project has current status :)
So summarizing:
I want to have got a table with relation many to many and with additional column named ApplicationStatus. Is it possible to get this on auto generated table, or maybe i should create it by hand somehow?

Yes, that is possible. This should help.
Entity Framework : Customized Join Table in a Many to Many Relationship

Related

Implementing Many-To-Many relationship in Asp.Net (confused?)

My current aim is to build a database structure using classes in Entity Framework & ASP MVC.
I currently have a Users table and a Posts table. What I would like to do is create a many to many relationship for Users who have liked Posts (whilst conserving who created the post). And be able to access for each user all of the posts they have liked. Currently I have these classes but I'm unsure of how to link them as all of the online examples are linking Primary Keys from different databases where I just want to use the Username Parameter. Any help would be great. I have this so far.
public class Posts
{
[Key]
public virtual int PostId { get; set; }
public virtual string Title { get; set; }
public virtual string URL { get; set; }
[DisplayName("Main Text")]
public virtual string TextBody { get; set; }
public int PostLikes { get; set; }
private DateTime Datedata = DateTime.Now;
public DateTime PostDate { get { return Datedata; } set { Datedata = value; } }
public virtual Users User { get; set; }
public ICollection<PostLikes> UsersWhoHaveSigned { get; set; }
}
{
public class Users
{
[Key]
public virtual int UserId { get; set; }
public virtual string Username { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual List<Posts> Post { get; set; }
}
}
I have not built the UsersWhoHaveSigned table yet. Early experimentation caused me so much backtracing it was painful. Any help would be great.
Edit: I was hoping to ask for help and then appropriate that informtaion to fit my example which utilises the individual accounts add-on. This produces some addition files that are now causing interference with the code you've provided.
Here is the IdentityModels.cs file.
using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
namespace Coursework2.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit https://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}
I believe that the assembly directives at the top are preventing system.data.entity from being used so when I try to implement ApplicationDbContext : DbContext I get error messages :/
Ideally I'm looking to use the IdentityModels.cs file as a replacement for the users class. But still very lost.
First of all, I recommend that you use the singular form for your class names, as EF will automatically pluralize table names.
Second, for a key property, you can just use the term Id, without any annotations, and EF will pick it up as the principal key.
Finally, I'll assume you are looking to use a Code-First approach. Consider the following classes (yours, but refactored for clarity purpose):
public class Post
{
public virtual Guid Id { get; set; }
public virtual string UserName { get; set; }
public virtual User User { get; set; }
public virtual ICollection<PostLike> Likes { get; set; }
}
public class PostLike
{
public virtual Guid Id { get; set; }
public virtual Guid PostId { get; set; }
public virtual Post Post { get; set; }
public virtual string UserName { get; set; }
public virtual User User { get; set; }
}
public class User
{
public virtual Guid Id { get; set; }
public virtual string UserName { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<PostLike> Likes { get; set; }
}
To make it work, you'd need a DbContext such as the following. Pay attention to the OnModelCreating method, which is where the magic happens:
public class ApplicationDbContext
: DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<PostLike> PostLikes { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasAlternateKey(u => u.UserName);
modelBuilder.Entity<User>()
.HasMany(u => u.Posts)
.WithOne(p => p.User);
modelBuilder.Entity<Post>()
.HasOne(p => p.User)
.WithMany(u => u.Posts)
.HasForeignKey(p => p.UserName)
.HasPrincipalKey(u => u.UserName);
modelBuilder.Entity<Post>()
.HasMany(p => p.Likes)
.WithOne(pl => pl.Post);
modelBuilder.Entity<PostLike>()
.HasOne(pl => pl.Post)
.WithMany(p => p.Likes);
modelBuilder.Entity<PostLike>()
.HasOne(pl => pl.User)
.WithMany(u => u.Likes)
.HasForeignKey(pl => pl.UserName)
.HasPrincipalKey(u => u.UserName);
}
}
Voila! I hope it answers your question ;)
If so, please don't forget to mark my post as the answer!
Edit:
I'll provide some explanations, that I had left out to answer your question ASAP.
So, first thing you need to do, is to declare the UserName as an alternate key, because you want to create relationships depending on it, and you already have the 'Id' principal key declared.
Second, on each object that should own a User reference base on the UserName alternate key, you need to declare the object's UserName property as the foreign key of the relationship, and the User's UserName property as the principal key.
In other words, the foreign key is the property that a referencing object uses for the relationship, and the principal key is the property based on which the referenced object is bound to the referencing one.
Note that principal keys must have a key or alternate key constraint, or it won't work.
Just to comment on your answer. I found that I had to use
using Microsoft.EntityFrameworkCore and remove System.Data.Entity - This was causing the program to be confused as to which DbContext I wanted to use. Thanks!

The INSERT statement conflicted with the FOREIGN KEY constraint: Need data to be saved into two databases at the same time

I have two Models, PurchaseOrders and PurchaseOrderMessages as shown below. I am implementing this in such a way that a user, while creating a purchase order, enters the purchase order details, PurchaseOrderName and a PurchaseOrderMessage. I am using my PurchaseOrderVM for the View. On submitting the details, in the POST method, like this:
_context.Add(purchaseOrdersObj);
_context.Add(purchaseOrdersMessagesObj);
_context.SaveChanges();
I am getting the following error: The INSERT statement conflicted with the FOREIGN KEY constraint. I understand why I am getting that error. It's becuase my FOREIGN KEY, PurchaseOrderId is not present in the database yet, as the data for PurchaseOrders has not been saved.
Is there a way in which I can save the Message to the PurchaseOrderMessages the same time I save details for PurchaseOrders? Also, it has to be done in such a way that the user should be able to add more PurchaseOrderMessages in the future to the same PurchaseOrder, once the PurchaseOrder has been created. And, all the PurchaseOrderMessages should be saved so that they can all be printed on the screen one after another.
PurchaseOrders.cs
public class PuchaseOrders
{
[Key]
public int PurchaseOrderId { get; set; }
public string PurchaseOrderName { get; set; }
}
PurchaseOrderMessages.cs
public class PurhcaseOrderMessages
{
[Key]
public int UpdateId { get; set; }
public string Message { get; set; }
[ForeignKey("PurchaseOrder")]
public int PurchaseOrderId { get; set; }
public virtual PurchaseOrder PurchaseOrder { get; set; }
}
PurchaseOrderVM.cs
public class PurchaseOrderVM
{
public int PurchaseOrderId { get; set; }
public string PurchaseOrderName { get; set; }
public string Message { get; set; }
}
You need to allow EF to wire this up, so it can handle saving the objects in the right order. Instead of setting an id, set the navigation property:
purchaseOrdersMessagesObj.PurchaseOrder = purchaseOrdersObj;
_context.Add(purchaseOrdersMessageObj);
_context.SaveChanges();
You can do this by two solutions:
First:
public class PuchaseOrders
{
public PuchaseOrders(){
Messages = new List();
}
[Key]
public int PurchaseOrderId { get; set; }
public string PurchaseOrderName { get; set; }
public ICollection<PurhcaseOrderMessages> Messages{ get; set; }
}
PuchaseOrders purchaseOrdersObj= new PuchaseOrders(){ PurchaseOrderName = "Pla Pla"};
purchaseOrdersObj.Messages.Add(new PurhcaseOrderMessages(){Message = "Pla Pla"});
_context.Add(purchaseOrdersObj);
_context.SaveChanges();
Second: if you work with SP (stored procedures) in this project a lot you can write SP to take the two objects data and insert them

EF6 I have GUID for UserId Stored in a table but want to add ApplicationUser to the Model

My Application is all fine and within IdentityModels I set each class (table). But I want to show in my Razor View the UserName from AspNetUsers and not the GUID. I currently store the GUID but thats all i'm able to display in the views
I'm thinking there must be a built in easy way to do this - and that I don't need to do any mapping or do i
I'm using EF6 and MVC4.5
Here is my class :
public partial class x23BatchImport
{
public int x23BatchImportId { get; set; }
public Nullable<DateTime> DateTimeFromFilename { get; set; }
public string UserId { get; set; }
public string Filename { get; set; }
public decimal? Length { get; set; }
public Nullable<DateTime> StartDateTime { get; set; }
public Nullable<DateTime> StopDateTime { get; set; }
//public virtual ApplicationUser ApplicationUser { get; set; }
}
...here is an extract from IdentityModels.cs
namespace AscendancyCF.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
//public System.Data.Entity.DbSet<AscendancyCF.Models.ApplicationUser> ApplicationUser { get; set; }
public System.Data.Entity.DbSet<AscendancyCF.Models.SupplyPointType> SupplyPointTypes { get; set; } ETC ETC
.....NB all my tables are declared here then I use OnModelCreating to set up relationships some of the time...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Reference : http://blogs.msdn.com/b/adonet/archive/2010/12/06/ef-feature-ctp5-fluent-api-samples.aspx
base.OnModelCreating(modelBuilder);
// Configure the 1-1
modelBuilder.Entity<SupplyPoint>()
.HasOptional(a => a.SupplyPointAddress)
.WithMany()
.HasForeignKey(u => u.SupplyPointAddressId);
}
Entity Framework has a set of model and property naming conventions that it uses by default. Currently, it's not able to figure out that UserId is a foreign key to an ApplicationUser.
If you don't want to add any manual mappings, you have to change your naming. The simplest would be to rename the ApplicationUser property to User.
public virtual ApplicationUser User { get; set; }
When doing your queries, use Include() to eager load the User property with the matching ApplicationUser...
// using System.Data.Entity; // add this using to use Include()
var context = new ApplicationDbContext();
var batchImport = context.x23BatchImport
.Include(x => x.User)
.(x => x.x23BatchImportId == 1)
.Single();
var username = batchImport.User.UserName;
Your other alternatives are:
Change UserID property to ApplicationUserId
Specify the foreign key in a manual mapping in OnModelCreating()
Specify the foreign key on the model using data annotations

get Membership user object from another model

i want something like this :
public class Order
{
public Guid OrderID { get; set; }
public Guid UserId { get; set; }
public DateTime OrderDate { get; set; }
public decimal Amount { get; set; }
public virtual ICollection<OrderDetail> orderDetailByOrderID { get; set; }
public virtual MembershipUser userByOrderID { get; }
}
so from above code i want membership user to be access from Order object ....
however i tried it but its not working.
so please suggest some solution if you have come across this type situation
It sounds like you might be using Entity Framework Code First. If this is the case you'd probably want:
public virtual aspnet_Membership userByOrderID { get; set; }
instead of
public virtual MembershipUser userByOrderID { get; }
That would grab the aspnet_Membership entity that is tied to the UserId foreign key. The aspnet_Membership class is not quite the same as the MembershipUser entity, but they have many of the same properties.
If that won't work, you can always use your Order model as is, and generate a ViewModel that has the MembershipUser object.
public class OrderViewModel
{
public Order Order { get; set; }
public MembershipUser User { get; set; }
}
and create the ViewModel like this before passing it into a view
Order order = EntityDataContext.Orders.First();
var model = new OrderViewModel { Order = order, User = Membership.GetUser(order.UserId) }

Using VB.NET MVC3 and the Entity Framework "Code-First" method, how can I easily define multiple one-to-many relationships with the same model?

I'm very new to ASP.NET and could use some help.
For this scenario, I have 2 classes. One is a "project" class and the other is a "company" class. Essentially, what I need is one single "company directory" of all the companies we have relationships with, but I need to be able to freely slot them into 3 different slots within a project. It is possible that the same company could occupy all 3 slots, but it's equally likely that a different company will be placed in each slot.
Here are my classes:
public class Project
{
public int ID { get; set; }
public string Name { get; set; }
public int ClientID { get; set; }
public int PublisherID { get; set; }
public int DeveloperID { get; set; }
public Company Client { get; set; }
public Company Publisher { get; set; }
public Company Developer { get; set; }
}
public class Company
{
public int ID { get; set; }
public string Name { get; set; }
}
When I have used this basic outline in the past, the complex types I specify in the bottom half of the model definition will be auto generated based on the matching int ID properties specified earlier. For example, If I had a complex type "User" that was drawing it's data from a user table in my database, specifying (int UserID) within my class followed by (User User), the UserID field would be the actual field in my project table and the User object I specify will automatically be an object containing all the User information from the user table.
Using this method as I did in the classes specified above, however, does not work in the way I expected and instead creates not only ClientID, PublisherID, and DeveloperID but also creates CompanyID, CompanyID1, and CompanyID2 which are the fields that will actually be used when attempting to instantiate the Company objects I specified (even though those fields will contain null always).
Is there any way around this?
You just need to specify that your int properties are the foreign keys to your navigation properties.
public class Project
{
public int ID { get; set; }
public string Name { get; set; }
public int ClientID { get; set; }
public int PublisherID { get; set; }
public int DeveloperID { get; set; }
[ForeignKey("ClientID")]
public Company Client { get; set; }
[ForeignKey("PublisherID")]
public Company Publisher { get; set; }
[ForeignKey("DeveloperID")]
public Company Developer { get; set; }
}

Resources