I am creating a table such as this:
public class Team
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string CaptainId { get; set; }
public string CoCaptainId { get; set; }
public string ContactDetails { get; set; }
}
Then I have a table such as this:
public class TeamMember
{
public string Id { get; set; }
public string Description { get; set; }
public string GameDisplayName { get; set; }
public DateTime Joined { get; set; }
public string TeamId { get; set; }
}
I have 2 questions that I do not understand around EF 6 and MVC5.
1) How do I reference my Id field in Team table. Do I set TeamId inside TeamMember as a string or as a Guid? I understand I will need to set the attribute [ForeignKey("Team")] however I still do not understand how to properly reference to it in the code because whenever I need to do any type of comparison, I always have to type .ToString() on the Guid to be able to get the value to compare against another string value.
2) My TeamMember also has an Id, and this Id references User in Identity framework. How should I reference that one, the Id is meant to be a foreign key that references the Id in User table, but I do not know how to properly reference that one either, is it string like I did it or is it Guid or something else?
EDIT:
public class Team
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid CaptainId { get; set; }
public Guid CoCaptainId { get; set; }
}
public class TeamMember
{
public Guid MemberId { get; set; }
public string TeamId { get; set; }
public virtual Team Team { get; set; }
public virtual Member Member { get; set; }
}
public class Member : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string About { get; set; }
public string Alias { get; set; }
public string CustomUrl { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<Member> 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<Member>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<Team> Teams { get; set; }
public DbSet<TeamMember> TeamMembers { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUserClaim>().ToTable("MemberClaim");
modelBuilder.Entity<IdentityUserRole>().ToTable("MemberRole");
modelBuilder.Entity<IdentityUserLogin>().ToTable("MemberLogin");
modelBuilder.Entity<IdentityRole>().ToTable("Role");
modelBuilder.Entity<Member>().ToTable("Member");
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Error that I get:
var user = new Member { UserName = model.Email, Email = model.Email, CustomUrl = model.CustomUrl, Alias = model.Alias};
var team = new Team {CaptainId = user.Id, Created = currentTime, IsSingleMember = true};
CaptainId = user.Id gives an error:
Cannot convert source type 'string' to target type 'System.Guid'
You should use the same type, which is Guid.
public Guid TeamId { get; set; }
Also, you need to keep a virtual Team property of Team type in your TeamMember model. Also typically int or long or Guid are the types used for primary key of a table. string might not be a good idea as you need to execute your custom code to generate a string which does not exist in the table.
This will generate the 2 tables with proper foreign key relationships.
public class Team
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string CaptainId { get; set; }
public string CoCaptainId { get; set; }
public string ContactDetails { get; set; }
}
public class TeamMember
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public string Description { get; set; }
public string GameDisplayName { get; set; }
public DateTime Joined { get; set; }
public Guid TeamId { get; set; }
public virtual Team Team { set; get; }
}
You do not need to do a ToString() conversion now for comparison. You can do YourGuid1==YourGuid2 expression
Related
i am designing a system and one of my entity has one to many relation as shown below.
public class Product
{
public int Id { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public Product Product { get; set; }
}
competitorProduct indicates that product has a equivalent which is sold by different store. should i define one-to-many relation as shown above or below? which one is correct?
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts{ get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
}
Assuming it is a one to many relationship (what would happen if a competitor product was competing with more than one of your products for example) you can do both and add in a foreign key as well.
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
You can then set up your relationship using fluent API as so:
modelBuilder.Entity<CompetitorProduct>(entity =>
{
entity.HasOne(e => e.Product)
.WithMany(e => e.CompetitorProducts)
.HasForeignKey(e => e.ProductId)
.HasConstraintName("FK_ComptetitorProduct_Product");
});
This way you can access the competitor products from the product and the product from the competitor products.
Here is a quick example of a ecommerce site I have worked on and how we did table relations.
I removed a bunch of the fields so you can see what you really need. Once to make relations and run Add-Migration EF will handle the FK constraints for you as long as you identified them in models like how I have below.
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Active = true;
CreateDateTimeUtc = DateTime.UtcNow;
ModifiedDateTimeUtc = DateTime.UtcNow;
}
[StringLength(500)]
public string FirstName { get; set; }
[StringLength(500)]
public string LastName { get; set; }
[StringLength(1000)]
public string Address { get; set; }
[StringLength(100)]
public string Unit { get; set; }
[StringLength(250)]
public string City { get; set; }
[StringLength(25)]
public string State { get; set; }
[StringLength(20)]
public string ZipCode { get; set; }
//This will give access to a list of child carts a user could have
[Index]
public bool Active { get; set; }
public virtual ICollection<Cart> Carts { get; set; }
// Account Profile Image
public byte[] ProfileImage { get; set; }
[StringLength(500)]
public string ProfileFilename { get; set; }
[StringLength(100)]
public string ProfileMimeType { get; set; }
}
[Table("Cart", Schema = "dbo")]
public class Cart : AbstractTable
{
public Cart()
{
IsComplete = false;
}
//This create relation to user table where I can get one unique user.
[StringLength(128)]
[ForeignKey("ApplicationUser")]
public string UserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
//These link us to child tables of Cart where we can get a LIST of the items below
public virtual ICollection<CartCategory> CartCategories { get; set; }
public virtual ICollection<CartItem> CartItems { get; set; }
// Marked when a payment/receipt is generated based off of this cart
public bool IsComplete { get; set; }
}
[Table("CartItem", Schema = "dbo")]
public class CartItem : AbstractTable
{
//This will return one unique cart id and let us access it as the parent record
[ForeignKey("Cart")]
public Guid CartId { get; set; }
public virtual Cart Cart { get; set; }
// Signifies if this was paid for in a receipt
public bool IsComplete { get; set; }
public virtual ICollection<CartItemCustomField> CustomFields { get; set; }
}
Ok so I have a relationship between the ApplicationUser and QuestionResults, my models are as below, the userId nor the UserName is retrieved, but I really need the UserId setup as a foreignKey on the QuestionResults entity.
Any help is much appreciated the error that I am receiving is as below:
An exception of type 'System.NullReferenceException' occurred in STRA.dll but was not handled in user code
Additional information: Object reference not set to an instance of an object.
on these lines of code:
qr.User.Id = User.Identity.GetUserId();
qr.User.UserName = User.Identity.GetUserName();
Models
public class QuestionResult
{
public QuestionResult()
{
DateCreated = DateTime.Now;
DateModified = DateTime.Now;
}
public int Id { get; set; }
public DateTime? DateCreated { get; set; }
public DateTime? DateModified { get; set; }
public int QuestionScore { get; set; }
//navigation properties
public virtual ApplicationUser User { get; set; }
//public ICollection<CategoryResult> CategoryResult { get; set; }
//public virtual Category Category { get; set; }
[NotMapped]
public int CategoryId { get; set; }
public virtual Question Question { get; set; }
public int QuestionId { get; set; }
//public virtual Category Category { get; set; }
//public int CategoryId { get; set; }
}
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string Surname { get; set; }
public string Industry { get; set; }
public string GlobalRegion { get; set; }
public string CurrentSituation { get; set; }
public int SalesForceSize { get; set; }
public bool IsVerified { get; set; }
//navigation properties
public virtual ICollection<CategoryResult> CategoryResult { get; set; }
public virtual ICollection<QuestionResult> QuestionResult { get; set; }
//public virtual ICollection<Report> Report { get; set; }
//public virtual ICollection<SurveyResult> SurveyResult { get; set; }
public virtual Organisation Organisation { get; set; }
public int? OrganisationId { get; set; }
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;
}
}
Your code is equivalent to this::
var user = qr.User;
user.Id = User.Identity.GetUserId();
If the QuestionResult was already linked to a User then you would not be changing which User is linked to the QuestionResult, you would be changing the Id of an existing User - and that is not allowed anyway.
But the QuestionResult is not already linked to a User. qr.User is null - so you get a null reference exception.
In general, life is much easier in Entity Framework if you add the foreign key to your model:
public class QuestionResult
{
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
}
And now you can set the foreign key directly:
qr.UserId = User.Identity.GetUserId();
References:
Why does Entity Framework Reinsert Existing Objects into My Database?
Making Do with Absent Foreign Keys
So, do you wanna make foreing key for userid?
You can do like that:
public int UserRefID { get; set; }
[ForeignKey("UserRefID")]
public xxx UserID { get; set; } //data name like ApplicationUser
And this error appear coz you have some problem about models or data classes.
Set it as a string
Model:
public ApplicationUser User { get; set; }
public string UserId { get; set; }
Controller:
qr.UserId = User.Identity.GetUserId();
And worked perfectly, even created foreign keys, that easy. Thanks so much!
I have a setup like this:
[Table("tablename...")]
public class Branch
{
public Branch()
{
Users = new List<User>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public List<User> Users { get; set; }
}
[Table("tablename...")]
public class User
{
[Key]
public int Id {get; set; }
public string Username { get; set; }
public string Password { get; set; }
[ForeignKey("ParentBranch")]
public int? ParentBranchId { get; set; } // Is this possible?
public Branch ParentBranch { get; set; } // ???
}
Is it possible for the User to know what parent branch it belongs to? The code above does not populate the ParentBranch.
Entity Framework version 5.0
.NET 4.0
c#
Try making navigation properties virtual,
[Table("tablename...")]
public class Branch
{
public Branch()
{
Users = new List<User>();
}
[Key]
public int Id { get; set; }
public string Name { get; set; }
public virtual List<User> Users { get; set; }
}
[Table("tablename...")]
public class User
{
[Key]
public int Id {get; set; }
public string Username { get; set; }
public string Password { get; set; }
[ForeignKey("ParentBranch")]
public int? ParentBranchId { get; set; } // Is this possible?
public virtual Branch ParentBranch { get; set; } // ???
}
I have an Account object that contains an "Id" field, which is mapped to a View in the database:
public class Account : GeneralInfo
{
[Column("first_name")]
public string FirstName { get; set; }
[Column("last_name")]
public string LastName { get; set; }
public string Designation { get; set; }
[Column("full_name")]
public string FullName { get; set; }
public string Email { get; set; }
[Column("member_type")]
public string MemberType { get; set; }
[Column("status")]
public string Status { get; set; }
[Column("paid_thru")]
public DateTime? PaidThru { get; set; }
[Column("member_record")]
public bool MemberRecord { get; set; }
[Column("category")]
public string Category { get; set; }
public virtual Subscription Subscriptions { get; set; }
}
I also have a Subscription object that uses the same "Id" as the account object:
[Table("Subscriptions")]
public class Subscription
{
[Column("Id")]
public string ID { get; set; }
[Column("Balance")]
public decimal Balance { get; set; }
}
When I try to use "subscription" as a navigation property of account, I get an error saying: {"Invalid column name 'Subscriptions_ID'."}
How can I access "subscriptions" using the Account object?
Use the fluent API to map the Shared PK relationaship.
public class MyContext : DbContext
{
// ...........
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Subscription>().HasRequired()
.WithOptional(a => a.Subscriptions);
}
}
I have a working model, but have noticed that the relationship has been created twice in the database. Originally, it created two columns in the table, but with the addition of a specified foreign key attribute it has now just the one.
I have an Account class, which has many employers who can use the account. (one to many) Here are the classes:
public class Account
{
public int AccountId { get; set; }
[Required(ErrorMessage = Constants.ValidationMessages.FieldRequired)]
public string Name { get; set; }
public int? PrimaryUserId { get; set; }
[ForeignKey("PrimaryUserId")]
public Employer PrimaryUser { get; set; }
[ForeignKey("EmpAccountId")]
public ICollection<Employer> Employers { get; set; }
}
here is the inherited Employer class
public class Employer : User
{
public Employer()
{
DepartmentsToPost = new Collection<Department>();
Contacts = new Collection<Contact>();
}
[Display(Name = "Workplaces to advertise jobs")]
public virtual ICollection<Department> DepartmentsToPost { get; set; }
public int EmpAccountId { get; set; }
[ForeignKey("EmpAccountId")]
public virtual Account Account { get; set; }
public override string UserType
{
get { return "Employer"; }
}
}
User Table:
UserId
Username
FirstName
Surname
EmpAccountId
Discriminator
Account Table
AccountId
Name
PrimaryUserId
There is one link back to the User table - this is for the PrimaryUser field, and this is correct. There are two other relationships: Account -> Employers. EF has named them Account_Employers and Employer_Account. These are duplicates.
How can I prevent this occuring?
The Employers collection should be decorated with InversePropertyAttribute to point to the navigational property on the other side.
public class Account
{
public int AccountId { get; set; }
[Required(ErrorMessage = Constants.ValidationMessages.FieldRequired)]
public string Name { get; set; }
public int? PrimaryUserId { get; set; }
[ForeignKey("PrimaryUserId")]
public Employer PrimaryUser { get; set; }
[InverseProperty("Account")]
public ICollection<Employer> Employers { get; set; }
}