I have a UserProfile class
public class UserProfile
{
public UserProfile()
{
}
public UserProfile(string userId)
{
AppUserId = userId;
}
[Key]
public int UserProfileId { get; set; }
public string AppUserId { get; set; }
public ICollection<Blog> AuthoredBlogs { get; set; }
public ICollection<Blog> SubscribedBlogs { get; set; }
//other properties removed for brevity
}
And the associated Blog class
public class Blog
{
[Key]
public int BlogId { get; set; }
[ForeignKey("BlogAuthor")]
[Index("IX_AuthorIndex", 1, IsClustered = false, IsUnique = false)]
public int AuthorId { get; set; }
[Required]
public Author BlogAuthor { get; set; }
[Required(ErrorMessage = "A blog name is required")]
public string BlogName { get; set; }
public string BlogIconUrl { get; set; }
public List<BlogPost> BlogPosts { get; set; }
public EquipmentCategory EquipmentCategory { get; set; }
public EquipmentType EquipmentType { get; set; }
public ICollection<int> BlogReaderIds { get; set; }
public Blog(string name, Author author)
{
BlogName = name;
BlogAuthor = author;
EquipmentType = EquipmentType.NoSearch;
EquipmentCategory = EquipmentCategory.NoSearch;
}
public Blog()
{
EquipmentType = EquipmentType.NoSearch;
EquipmentCategory = EquipmentCategory.NoSearch;
}
}
I am having a hard time figuring out how to model the two collections in UserProfile (AuthoredBlogs and SubscribedBlogs) with Blog class. Having those two collections in UserProfile would require two FK associations to Blog but I just dont see how that can/should work.
A UserProfile can subscribe to and author many Blogs. But the Blog class can only have one author and either a list of subscribed UserProfiles or , as I have it here, a list of the subscriber's UserProfileId's.
I cant get it to work, the code first updates are failing to deploy to the db due to the FK association issues.
Any help appreciated.
These models annotations will create one-to-many relation between autor and blogs and many-to-many relation between blogs and subscribers via autocreated, shadow table.
public class UserProfile
{
//other stuff...
[InverseProperty("Autor")]
public ICollection<Blog> AuthoredBlogs { get; set; }
[InverseProperty("SubscribedUserProfiles")]
public ICollection<Blog> SubscribedBlogs { get; set; }
}
public class Blog
{
//other stuff..
public ICollection<UserProfile> SubscribedUserProfiles { get; set; }
public UserProfile Autor { get; set; }
[ForeignKey("Autor")]
public int AutorId { get; set; }
}
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; }
}
I'm new to MVC and I'm following a guide to make a MVC project with code first and Individual User Accounts Authentication on the Internet. But when I change the model to a many-to-many relationship, I get the
MyFirstASP_MVC_API.Models.BookAuthor: : EntityType 'BookAuthor' has no key defined. Define the key for this EntityType.
BookAuthors: EntityType: EntitySet 'BookAuthors' is based on type 'BookAuthor' that has no keys defined.
Here are my classes
public class Author
{
public int Id { get; set; }
[Required]
[StringLength(100)]
public string Name { get; set; }
[StringLength(1000)]
public string Description { get; set; }
public bool IsActive { get; set; }
public ICollection<BookAuthor> BookAuthors { get; set; }
}
public class Book
{
public int Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
[Required]
[Index(IsUnique = true)]
[StringLength(20)]
public string ISBN { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public Nullable<DateTime> CreatedDate { get; set; }
public Nullable<DateTime> ModifiedDate { get; set; }
public bool IsActive { get; set; }
public ICollection<BookAuthor> BookAuthors { get; set; }
public ICollection<BookCategory> BookCategories { get; set; }
public Publisher Publisher { get; set; }
public int PublisherId { get; set; }
}
public class BookAuthor
{
public int BookId { get; set; }
public Book Book { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
I used ApplicationDbContext which is in IdentityModel.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Book> Books { get; set; }
public DbSet<Author> Authors { get; set; }
public DbSet<Publisher> Publishers { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<BookCategory> BookCategories { get; set; }
public DbSet<BookAuthor> BookAuthors { get; set; }
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
I've read some article about solving the problem, they used OnModelCreating but none of them using the ApplicationDbContext so I don't know I should really add it into the ApplicationDbContext.
So what should I do, do I need to create new DbContext ???
An Author can have multiple books so you should define a collection of Books in your Author Entity:
public ICollection<Book> Books { get; set; }
A book can actually have more than 1 Author in reality (not sure if it is the same in your case) but if it is, you should declare a collection of Authors in your Book Entity:
public ICollection<Author> Authors { get; set; }
Thats it, this will yield you the following tables:
In addition, if you want to learn more there are really a ton of sources available:
https://www.entityframeworktutorial.net/
https://www.tutorialspoint.com/entity_framework/
https://learn.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application
Good luck!
I have many classes representing tables, but three are giving me headaches: Person, Task, and Role, here is their code:
public class Person : BaseModel
{
public int Id { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public string Title { get; set; }
public ICollection<TestEvent> TestEventsLed { get; set; }
public ICollection<TestEvent> TestEventsCreated { get; set; }
public ICollection<Program> ProgramsLed { get; set; }
public ICollection<Task> TasksCreated { get; set; }
public ICollection<PersonalEvent> PersonalEventsCreated { get; set; }
public virtual ICollection<Role> RolesHeld { get; set; }
public virtual ICollection<Task> TasksAssigned { get; set; }
}
public class Role : BaseModel
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> PeopleWithThisRole { get; set; }
}
public class Task : BaseModel
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime SuspenseDatetime { get; set; }
public DateTime CreatedDatetime { get; set; }
public int CreatedById { get; set; }
public bool Completed { get; set; }
public bool Archived { get; set; }
public Person CreatedBy { get; set; }
public virtual ICollection<Person> PeopleAssigned { get; set; }
}
What I end up with is mostly what I wanted, except a few hiccups:
Expected: Actual:
- People should have 0 foreign keys, just - People has 1 FK and 1 extra column out of
2 many-to-manys for RolesHeld and nowhere: Task_Id and the FK is for that
TasksAssigned new column referencing Id in Tasks?
- Task should have 1 foreign key for - Task has 2 extra columns out of nowhere
CreatedById linked to a Person called Person_Id and Person_Id1 and then
identical foreign keys attached to them
(and it has the expected CreatedById FK)
- There should be a RolePersons table - This part happened correctly and with the
with 2 FKs to represent the many-to-many correct FKs to represent the many-to-many
- There should be a TaskPersons table - There is no new table at all for this
with 2 FKs to represent the many-to-many
The weird thing is, I did some of these the same way (like the two many-to-many relationships) but then only 1 turned out correctly? Can you see what I did incorrectly?
Sometime default mapping is not what we want, so we have to explicitly say to EF what we need. Just add this method to your DbContext and it works as required:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>().HasMany(p => p.TasksAssigned).WithMany(t => t.PeopleAssigned);
modelBuilder.Entity<Person>().HasMany(p => p.TasksCreated).WithRequired(t => t.CreatedBy).WillCascadeOnDelete(false);
}
Entity Framework do something by convention.
Look your Task class and Person class
public class Task : BaseModel
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime SuspenseDatetime { get; set; }
public DateTime CreatedDatetime { get; set; }
public int CreatedById { get; set; }
public bool Completed { get; set; }
public bool Archived { get; set; }
public Person CreatedBy { get; set; }
public virtual ICollection<Person> PeopleAssigned { get; set; }
}
public class Person : BaseModel
{
public int Id { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public string Title { get; set; }
public ICollection<TestEvent> TestEventsLed { get; set; }
public ICollection<TestEvent> TestEventsCreated { get; set; }
public ICollection<Program> ProgramsLed { get; set; }
public ICollection<Task> TasksCreated { get; set; }
public ICollection<PersonalEvent> PersonalEventsCreated { get; set; }
public virtual ICollection<Role> RolesHeld { get; set; }
public virtual ICollection<Task> TasksAssigned { get; set; }
}
In your Task Class you are putting Person object and as well as a collection of Person.That's the thing is the cause of your headache i guess.
If you need many to many relation between them,then you should not put this property inside your Task Class
public Person CreatedById { get; set; }
public Person CreatedBy { get; set; }
Or If you need one to many relation between them,then Remove this property form your Task class
public virtual ICollection<Person> PeopleAssigned { get; set; }
How can I build a model using Entity Framework to join 3 tables?
At the moment I have:
public class KeywordAdCategory
{
[Key]
[Column("Keyword_Id", Order = 0)]
public int Keyword_Id { get; set; }
[Key]
[Column("Ad_Id", Order = 1)]
public int Ad_Id { get; set; }
[Key]
[Column("Category_Id", Order = 2)]
public int Category_Id { get; set; }
}
But I don't have any navigation properties.
Is there a better way to build a relashionship between 3 tables using Entity Framework?
Also the Keyword, Ad and Category models:
public class Keyword
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class Ad
{
// Primary properties
[Key]
public int Id { get; set; }
// Navigation properties
public AdOperation AdOperation { get; set; }
public Member Member { get; set; }
public Address Address { get; set; }
public Category Category { get; set; }
public virtual ICollection<Picture> Pictures { get; set; }
private ICollection<Feature> _features;
public virtual ICollection<Feature> Features
{
get { return _features ?? (_features = new HashSet<Feature>()); }
set { _features = value; }
}
}
public class Category
{
// Primary properties
public int Id { get; set; }
public int? CategoryParent_Id { get; set; }
public int? CategoryGroup_Id { get; set; }
public bool IsActive { get; set; }
// Navigation properties
public Keyword Keyword { get; set; }
}
Thanks.
I'm assuming that you're using Code-First Entity Framework here, and that you have your KeywordAdCategory object in your database as well. In which case, just simply do the following in your KeywordAdCategory class to do the proper mapping:
[Key, ForeignKey("Keyword")]
[Column("Keyword_Id", Order = 0)]
public int Keyword_Id { get; set; }
[Key, ForeignKey("Ad")]
[Column("Ad_Id", Order = 1)]
public int Ad_Id { get; set; }
[Key, ForeignKey("Category")]
[Column("Category_Id", Order = 2)]
public int Category_Id { get; set; }
public virtual Keyword Keyword { get; set; }
public virtual Ad Ad { get; set; }
public virtual Category Category { get; set; }
Doing this should do the proper mappings, put FKs on your KeywordAdCategory table, and thus give you the ability to have good navigation properties to the other objects.
I'm trying to create a one-to-one relation between two tables, but as a result I have one-to-many. What is the problem with this code?
namespace EFCF_Demo.Models
{
public class Post
{
[Key]
public int ID { get; set; }
public string Title { get; set; }
public string MiniContent { get; set; }
public string Author { get; set; }
public DateTime PublishDate { get; set; }
public int Rating { get; set; }
public virtual Content MainContent { get; set; }
}
public class Content
{
public int ID { get; set; }
public virtual Post Post { get; set; }
public string FullContent { get; set; }
}
public class PostEntities : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Content> Contents { get; set; }
}
}
Don't you need PostId in the Content class, and ContentId in the Post class?
public class Content
{
[Key]
public int PostId { get; set; }
public virtual Post Post { get; set; }
public string FullContent { get; set; }
}
what about this:) This should do it.
Problem was resolved by removing
public DbSet<Content> Contents { get; set; }
After that we don't need to use the Fluent API but I have some problems with saving.
Try this:
http://social.msdn.microsoft.com/Forums/eu/adonetefx/thread/4b5a46e0-f5c2-4c38-a73c-0038eaef2537
How to declare one to one relationship using Entity Framework 4 Code First (POCO)