How to deal with Duplicated variables in EntityFrameWork Core - many-to-many

I need help figuring out how to prevent duplicates of a variable. I am building a blog, and don't want the same tags to be created over and over each time I use it.
class MyContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostId, t.TagId });
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId);
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId);
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public string Name { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Do I need to create a custom method to make this happen, or is there a way to get it done using Fluent APi?
If the tag already exists, I want to instead add the post to the already existing list of posts with that same tagName.

Related

Join two tables in EntityFramework

I have two tables:
Companies
Contacts with a one-to-many relations (many contacts in one Company)
Contacts table EDITED:
public class Contact
{
public Guid Id { get; set; }
public DateTime dateCreated { get; set; }
public DateTime updated { get; set; }
public Boolean hidden { get; set; }
//Personal Data
public string title { get; set; }
public string jobTitle { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public string department { get; set; }
public string email { get; set; }
public string logoUrl { get; set; }
public string personalRemarks { get; set; }
//Telephone list
public ICollection<Phone> Phones { get; set; }
//Addresses
public ICollection<Address> Addresses { get; set; }
//Bank Data
public ICollection<Bankdata> Bankdatas { get; set; }
//Tags
public ICollection<Tag> Tags { get; set; }
}
}
Companies table EDITED:
public class Organization
{
public Guid Id { get; set; }
public DateTime dateCreated { get; set; }
public DateTime dateUpdated { get; set; }
public Boolean hidden { get; set; }
//Company Data
public string organizationName { get; set; }
public string taxId { get; set; }
public string trades { get; set; }
public string organizationType { get; set; }
public string actionRadius { get; set; }
public string organizationRemarks { get; set; }
public string web { get; set; }
//Contacts
public ICollection<Contact> Contacts { get; set; }
//Tags
public ICollection<Tag> Tags { get; set; }
}
}
I have a method im my repository to select all the contacts in one company
public Organization GetOrganizationById(Guid Id)
{
return _context.Organizations
.Include(c => c.Contacts)
.Where(c => c.Id == Id)
.FirstOrDefault();
}
But since all I have is the company id I need to make a join between the two tables in order to get the name. Something like:
SELECT contacts,*, Organization.name
FROM contacts
INNER JOIN Organization ON Organization.id = Contacts.organization_id
WHERE Organization.id = id;
I have tried the following without success:
public Organization GetOrganizationById(Guid Id)
{
return _context.Organizations
.Include(o => o.organizationName)
.Include(c => c.Contacts)
.Where(c => c.Id == Id)
.FirstOrDefault();
}
Any help will be welcome
The right approach is to create a navigation property between the Organization class and the Contact class. The following code will let you understand the steps you need to do:
public class Contact
{
...
// Foreign key for Organization
public Guid OrganizationId { get; set; }
// Related Organization entity
[ForeignKey("OrganizationId ")]
public Organization Standard { get; set; }
}
public class Organization
{
...
// List of related Contacts
public virtual ICollection<Contact> Contacts { get; set; }
}
After creating a migration with this code you will implement your method as follows:
public Organization GetOrganizationById(Guid Id)
{
return _context.Organizations
.Include(c => c.Contacts)
.Where(c => c.Id == Id)
.FirstOrDefault();
}

unidirectional many-to-many relationship with Code First Entity Framework

I am new to EF, and trying to get many-to-many unidirectional relationship with code first approach. For example, if I have following two classes (not my real model) with be a N * N relationship between them, but no navigation property from "Customer" side.
public class User {
public int UserId { get; set; }
public string Email { get; set; }
public ICollection TaggedCustomers { get; set; }
}
public class Customer {
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
The mapping code looks like ...
modelBuilder.Entity()
.HasMany(r => r.TaggedCustomers)
.WithMany(c => c.ANavgiationPropertyWhichIDontWant)
.Map(m =>
{
m.MapLeftKey("UserId");
m.MapRightKey("CustomerId");
m.ToTable("BridgeTableForCustomerAndUser");
});
This syntax force me to have "WithMany" for "Customer" entity.
The following url, says "By convention, Code First always interprets a unidirectional relationship as one-to-many."
Is it possible to override it, or should I use any other approach?
Use this:
public class User {
public int UserId { get; set; }
public string Email { get; set; }
// You must use generic collection
public virtual ICollection<Customer> TaggedCustomers { get; set; }
}
public class Customer {
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
And map it with:
modelBuilder.Entity<User>()
.HasMany(r => r.TaggedCustomers)
.WithMany() // No navigation property here
.Map(m =>
{
m.MapLeftKey("UserId");
m.MapRightKey("CustomerId");
m.ToTable("BridgeTableForCustomerAndUser");
});

Entity Framework Code 1st - Mapping many-to-many with extra info

I've looked through several of the questions here and am not quite connecting all the (mental) dots on this. I would appreciate some help.
My Models (code first):
public class cgArmorial
{
[Key]
[Display(Name = "Armorial ID")]
public Guid ArmorialID { get; set; }
[Display(Name = "User ID")]
public Guid UserId { get; set; }
public string Name { get; set; }
public string DeviceUrl { get; set; }
public string Blazon { get; set; }
public virtual ICollection<cgArmorialAward> ArmorialAwards { get; set; }
}
public class cgArmorialAward
{
public cgArmorial Armorial { get; set; }
public cgAward Award { get; set; }
public DateTime AwardedOn { get; set; }
}
public class cgAward
{
[Key]
[Display(Name = "Award ID")]
public Guid AwardID { get; set; }
public string Name { get; set; }
public string Group { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public string Blazon { get; set; }
public virtual ICollection<cgArmorialAward> ArmorialAwards { get; set; }
}
Then in my Context class I have (last 2 entries):
public class Context : DbContext
{
public DbSet<cgUser> Users { get; set; }
public DbSet<cgEvent> Events { get; set; }
public DbSet<cgEventType> EventTypes { get; set; }
public DbSet<cgArmorial> Armorials { get; set; }
public DbSet<cgAward> Awards { get; set; }
public DbSet<cgArmorialAward> ArmorialAwards { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<cgUser>()
.HasMany<cgEvent>(e => e.EventAutocrats)
.WithMany(u => u.EventAutocrats)
.Map(m =>
{
m.ToTable("EventAutocrats");
m.MapLeftKey("UserId");
m.MapRightKey("EventId");
});
modelBuilder.Entity<cgUser>()
.HasMany<cgEvent>(e => e.EventStaff)
.WithMany(u => u.EventStaff)
.Map(m =>
{
m.ToTable("EventStaff");
m.MapLeftKey("UserId");
m.MapRightKey("EventId");
});
modelBuilder.Entity<cgArmorialAward>()
.HasRequired(a => a.Armorial)
.WithMany(b => b.ArmorialAwards);
modelBuilder.Entity<cgArmorialAward>()
.HasRequired(a => a.Award)
.WithMany(); // b => b.ArmorialAwards
}
}
I am getting this error when I try to run:
System.Data.Edm.EdmEntityType: : EntityType 'cgArmorialAward' has no
key defined. Define the key for this EntityType.
System.Data.Edm.EdmEntitySet: EntityType: EntitySet �ArmorialAwards�
is based on type �cgArmorialAward� that has no keys defined.
Well, as the exception says: You don't have a key defined on your entity cgArmorialAward. Every entity must have a key. Change it to the following:
public class cgArmorialAward
{
[Key, Column(Order = 0)]
[ForeignKey("Armorial")]
public Guid ArmorialID { get; set; }
[Key, Column(Order = 1)]
[ForeignKey("Award")]
public Guid AwardID { get; set; }
public cgArmorial Armorial { get; set; }
public cgAward Award { get; set; }
public DateTime AwardedOn { get; set; }
}
The fields in the composite key are foreign keys to the other two tables at the same time, hence the ForeignKey attribute. (I'm not sure if conventions would detect this automatically because you have non-standard names ("cgXXX" for the classes and "XXXId" for the foreign key properties). On the other hand the property names Armorial and Award match the foreign key property names. I'm not sure if EF conventions would consider this. So, perhaps the ForeignKey attribute is not necessary but at least it's not wrong.)

EF Code First Additional column in join table for ordering purposes

I have two entities that have a relationship for which I create a join table
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Image> Images { get; set; }
}
public class Image
{
public int Id { get; set; }
public string Filename { get; set; }
public virtual ICollection<Student> Students { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>()
.HasMany(i => i.Images)
.WithMany(s => s.Students)
.Map(m => m.ToTable("StudentImages"));
}
I would like to add an additional column to allow chronological ordering of the StudentImages.
Where should I add insert the relevant code?
Do you want to use that new column in your application? In such case you cannot do that with your model. Many-to-many relation works only if junction table doesn't contain anything else than foreign keys to main tables. Once you add additional column exposed to your application, the junction table becomes entity as any other = you need third class. Your model should look like:
public class StudentImage
{
public int StudentId { get; set; }
public int ImageId { get; set; }
public int Order { get; set; }
public virtual Student Student { get; set; }
public virtual Image Image { get; set; }
}
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentImage> Images { get; set; }
}
public class Image
{
public int Id { get; set; }
public string Filename { get; set; }
public virtual ICollection<StudentImage> Students { get; set; }
}
And your mapping must change as well:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<StudentImages>().HasKey(si => new { si.StudentId, si.ImageId });
// The rest should not be needed - it should be done by conventions
modelBuilder.Entity<Student>()
.HasMany(s => s.Images)
.WithRequired(si => si.Student)
.HasForeignKey(si => si.StudentId);
modelBuilder.Entity<Image>()
.HasMany(s => s.Students)
.WithRequired(si => si.Image)
.HasForeignKey(si => si.ImageId);
}

How do I get around this cyclical reference in EF?

I am using Entity Framework Code First with SQLCE in MVC3 for a blog-like site.
I am open to redesigning the structure if required, it would be great to get some help.
The context is set up as:
public class BinarContext : DbContext
{
public DbSet<Member> Members { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Reply> Replies { get; set; }
public BinarContext()
{
this.Configuration.LazyLoadingEnabled = true;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Member>().HasMany(member => member.Posts)
.WithRequired(post => post.Member)
.HasForeignKey(post => post.MemberID)
.WillCascadeOnDelete();
modelBuilder.Entity<Member>().HasMany(member => member.Replies)
.WithRequired(reply => reply.Member)
.HasForeignKey(reply => reply.MemberID)
.WillCascadeOnDelete();
modelBuilder.Entity<Post>().HasMany(post => post.Replies)
.WithRequired(reply => reply.Post)
.HasForeignKey(reply => reply.PostID)
.WillCascadeOnDelete();
base.OnModelCreating(modelBuilder);
}
}
*Note: * I have tried getting rid of WillCascadeOnDelete(); as suggested by others on SO but hasn't worked so far.
The models are:
The Member class has information about the posts and replies made by the member.
public class Member
{
public Guid ID {get; set;}
public string Username { get; set; }
public string Email { get; set; }
public virtual ICollection<Post> Posts { get; set; }
public virtual ICollection<Reply> Replies { get; set; }
}
The Post class that has information about the member who posted it and for the replied posted for it.
public class Post
{
public Guid ID {get; set;}
[DataType(DataType.MultilineText)]
public string Text { get; set; }
public Guid MemberID { get; set; }
public virtual Member Member { get; set; }
public virtual ICollection<Reply> Replies { get; set; }
}
The Reply class that has information about the member who posted it and for the post it was posted.
public class Reply
{
public Guid ID { get; set; }
[DataType(DataType.MultilineText)]
public string Text { get; set; }
public Guid PostID { get; set; }
public Guid MemberID { get; set; }
public virtual Post Post { get; set; }
public virtual Member Member { get; set; }
}
Thanks for your help :)
Try this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Member>().HasKey(x=>x.ID)
.HasMany(x => x.Posts)
.WillCascadeOnDelete();
modelBuilder.Entity<Member>().HasKey(x=>x.ID)
.HasMany(x => x.Replies)
.WillCascadeOnDelete();
modelBuilder.Entity<Post>().HasKey(x=>x.ID)
.HasMany(x => x.Replies)
.WillCascadeOnDelete();
modelBuilder.Entity<Post>().HasKey(x=>x.ID)
.WithRequired(x => x.Member)
.WithMany(x=>x.Posts);
modelBuilder.Entity<Replies>().HasKey(x=>x.ID)
.WithRequired(x => x.Member)
.WithMany(x=>x.Replies);
modelBuilder.Entity<Replies>().HasKey(x=>x.ID)
.WithRequired(x => x.Post)
.WithMany(x=>x.Replies);
base.OnModelCreating(modelBuilder);
}