MVC3 Entity Framework many-to-many with additional column - c#

I'm new to asp.net, mvc3 and entity framework.
I'm trying to develop a mvc3 programm with entity framework and code-first.
So I have two classes with a many-to-many relationship.
One class called "User" the other one is "Course".
public class Course : IValidatableObject
{
[...]
public virtual ICollection<User> Users { get; set; }
[...]
}
public class User : IValidatableObject
{
[...]
public virtual ICollection<Course> Courses { get; set; }
[...]
}
So, this works. But now I need an additional field which safes the status of the registration for a course.
Is there an easy way I don't know?
I tried it this way:
public class Course : IValidatableObject
{
[...]
public virtual ICollection<CourseUser> CourseUsers { get; set; }
[...]
}
public class User : IValidatableObject
{
[...]
public virtual ICollection<CourseUser> CourseUsers { get; set; }
[...]
}
public class CourseUser
{
[Key, ForeignKey("Course"), Column(Order = 0)]
public int Course_ID { get; set; }
[Key, ForeignKey("User"), Column(Order = 1)]
public string User_ID { get; set; }
public int Status { get; set; } //{ pending, approved }
public virtual Course Course { get; set; }
public virtual User User { get; set; }
}
But this makes it much more difficult to add or edit related data.
For example I didn't managed it yet to automatically add the user who created the course to the CourseUsers table.

No there is no easier way to do that. Once you add any additional field to your junction table it must be mapped as entity to allow you access to that field. It is not pure many-to-many relation any more. It is a new entity in your model with two one-to-many relations.

Related

Identity Framework 2 does not apply changes on new custom entity

I'm new to Identity Framework and maybe what I'm doing here is not the best approach, but either way, here is my scenario:
Beside the "Role" factor, some areas on my application should consider also if a given user is attached to a given "Company".
I created a new entity "Company" (very simple, only with Id and Name) and a relationship entity "UserCompany" (with the user and the company's Id). I tried to make it as similar as possible with the structure used between Roles and Users on Identity Framework.
In my ApplicationDbContext I added both DbSets and some logic for, for example, adding a list of companies to a user.
The problem that I'm facing is that "SaveChanges" does not apply the changes to the database.
*Edit: no error is thrown and the result of SaveChanges() is "1".
Here is a sample code from my ApplicationDbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Company> Companies { get; set; }
public DbSet<UserCompany> UserCompanies { get; set; }
//constructors, etc...
public void AddToCompanies(string _userId, params string[] _companyIds)
{
foreach (var companyId in _companyIds)
{
UserCompanies.Add(new UserCompany()
{
UserId = _userId,
DataAreaId = companyId
});
}
int result = this.SaveChanges();
}
}
And here is how I mapped this "UserCompany" entity
public class UserCompany
{
[Key, Column(Order = 1), ForeignKey("ApplicationUser")]
public string UserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
[Key, Column(Order = 2), ForeignKey("Company")]
public string DataAreaId { get; set; }
public virtual Company Company { get; set; }
}
On my UserAdminController class I created a private ApplicationDbContext object that is responsible for calling this logic. I suspect there is some problem in the way I'm dealing with two diferent contexts to save this data (one inside the ApplicationUserManager object and this new one), but I'm not sure if this is really the problem or if I'm missing something else here.
Any help would be appreciated!
Is the UserCompany model supposed to be like this
public class UserCompany
{
[Key, Column(Order = 1)]
public string UserId { get; set; }
[ForeignKey("UserId ")]
public virtual ApplicationUser ApplicationUser { get; set; }
[Key, Column(Order = 2)]
public string DataAreaId { get; set; }
[ForeignKey("DataAreaId")]
public virtual Company Company { get; set; }
}
And check if you use TransactionScope and not commit it.
Hope it helps!

EF Code First Flexible Relationships

I'm trying to set up a flexible one-to-many relationship in EF. Imagine we have the following POCO classes:
public class EntityA
{
public Guid Id { get; set; }
public virtual IEnumerable<Event> Events { get; set; }
}
public class EntityB
{
public Guid Id { get; set; }
public virtual IEnumerable<Event> Events { get; set; }
}
public class Event
{
public Guid Id { get; set; }
public Guid EntityId { get; set; }
}
I want the Event class to link to either Entity through the EntityId propery. So it's not a normal One-To-Many relationship.
I don't need any relationship properties on the Event side, but I would like there to be an IEnumerable<Event> Events property in each of the two Entity classes, which will allow me to grab a list of linked events for any entity I choose.
I'm using EntityTypeConfiguration<T> maps but I need help working out the relationships on this.

EF 6 - Cascade delete without fluent API [duplicate]

When using data annotations with EF4.1 RC is there an annotation to cause cascade deletes?
public class Category
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public ICollection<Product> Products { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
Using this model the constraint generated is:
ALTER TABLE [Product] ADD CONSTRAINT [Product_Category]
FOREIGN KEY ([Category_Id]) REFERENCES [Categorys]([Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
If not how is it achieved?
Putting required on the Product table Category relationship field solves this
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
[Required] //<======= Forces Cascade delete
public Category Category { get; set; }
}
I like to turn off cascade delete by default (by removing the OneToManyCascadeDeleteConvention)
I was then hoping to add them back in via annotations, but was surprised that EF doesn't include a CascadeDeleteAttribute.
After spending way too long working around EF's ridiculous internal accessor levels, the code in this gist adds a convention that allows attributes to be used: https://gist.github.com/tystol/20b07bd4e0043d43faff
To use, just stick the [CascadeDelete] on either end of the navigation properties for the relationship, and add the convention in your DbContext's OnModeCreating callback. eg:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Add<CascadeDeleteAttributeConvention>();
}
And in your model:
public class BlogPost
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[CascadeDelete]
public List<BlogPostComment> Comments { get; set; }
}
Not sure on Data Annotations, but you can add it in the database by modifying the actual relationship.
Looks like the answer is no for data annotations:
http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/394821ae-ab28-4b3f-b554-184a6d1ba72d/
This question appears to show how to do it with the fluent syntax, but not sure if that applies for 4.1 RC
EF 4.1 RC: Weird Cascade Delete
As an additional example to Tyson's answer, I use the [CascadeDelete] attribute like follows in an entity, which successfully adds the "Cascade" delete rule to the Parent-Child relation.
public class Child
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[SkipTracking]
public Guid Id { get; set; }
[CascadeDelete]
public virtual Parent Parent { get; set; }
[Required]
[ForeignKey("Parent")]
public Guid ParentId { get; set; }
}

1 to 1 required relationship with database generated identity

I have seen many examples of implementing a one to one relationship, but I failed doing mine, because the requirements are some kind different (Guid with database generated option, foreign key property and so on).
I have 2 classes (Bundesland, Programmkonfiguration) that have a 1:1 relationship (both ends are required in business sense) but cannot be joined into one table
Requirements to Bundesland:
Guid Id as Key but without a DatabaseGenerated Attribute
Navigation Property Programmkonfiguration
Bundesland.cs:
public class Bundesland
{
[Key]
public Guid Id { get; set; }
public virtual Programmkonfiguration Programmkonfiguration { get; set; }
}
Requirements to Bundesland
Guid Id as Key generated from Database
ForeignKey Property Bundesland_Id (needed with _ for interface)
Navigation Property Bundesland
Programmkonfiguration.cs:
public class Programmkonfiguration
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid Bundesland_Id { get; set; }
public virtual Bundesland Bundesland { get; set; }
}
database schema should look like this
table Bundesland (Id)
table Programmkonfiguration (Id, Bundesland_Id)
Why I failed until now:
EF doesn’t recognize the relationship by itself
if I use either attributes (ForeignKey, Required) or fluent API and the mode builder is not failing, the foreign key property Programmkonfiguration.Bundesland_Id is never set, after context.SaveChanges()
If you want to help me, here are additional classes you may gonna need: https://gist.github.com/anonymous/9cb554cd864e3dbee1ac
I am using .NET 4.5(.1) with EF5, but I failed with EF6 too
Thanks in advance :)
You can use fluent configuration for this:
public class Bundesland
{
[Key]
[ForeignKey("Programmkonfiguration")]
public Guid Id { get; set; }
public virtual Programmkonfiguration Programmkonfiguration { get; set; }
}
public class BundesLandConfiguration: EntityTypeConfiguration<Bundesland>
{
public BundesLandConfiguration()
{
HasProperty(p=>p.Id)
HasRequired(p=>p.Programmkonfiguration).WithRequiredPrincipal(p=>p.Bundesland);
}
}
public class Programmkonfiguration
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid Bundesland_Id { get; set; }
public virtual Bundesland Bundesland { get; set; }
}
public class ProgrammkonfigurationConfiguration: EntityTypeConfiguration<Programmkonfiguration>
{
public ProgrammkonfigurationConfiguration()
{
HasKey(p=>p.Id);
HasProperty(p=>p.Id)
HasProperty(p=>p.Bundesland_Id)
}
}
Do not forget to add this configurations to EntityModelConfigurations in db context.
Update: because property naming is against convention, you should add [ForeignKey] attribute as I added to property Id of Bundesland class.

EF Code First Many to many relation store additional data in link table

How do I store additional fields in the "link table" that is automagically created for me if I have two entities associated as having a many to many relationship?
I have tried going the "two 1 to many associations"-route, but I'm having a hard time with correctly configuring the cascading deletion.
Unless those extra columns are used by some functions or procedures at the database level, the extra columns in the link table will be useless since they are completely invisible at the Entity Framework level.
It sounds like you need to re-think your object model. If you absolutely need those columns, you can always add them later manually.
You will most likely need to expose the association in your domain model.
As an example, I needed to store an index (display order) against items in an many-to-many relationship (Project <> Images).
Here's the association class:
public class ProjectImage : Entity
{
public Guid ProjectId { get; set; }
public Guid ImageId { get; set; }
public virtual int DisplayIndex { get; set; }
public virtual Project Project { get; set; }
public virtual Image Image { get; set; }
}
Here's the mapping:
public class ProjectImageMap : EntityTypeConfiguration<ProjectImage>
{
public ProjectImageMap()
{
ToTable("ProjectImages");
HasKey(pi => pi.Id);
HasRequired(pi => pi.Project);
HasRequired(pi => pi.Image);
}
}
From Project Map:
HasMany(p => p.ProjectImages).WithRequired(pi => pi.Project);
Maps to the following property on project:
public virtual IList<ProjectImage> ProjectImages { get; set; }
Hope that helps
Ben
Suppose there is a many-to-many association between two types: User and Message, and the association class is defined as UserMessageLink with additional properties.
public class User {
public int Id {get;set;}
}
public class Message {
public int Id {get;set;}
}
//The many-to-many association class with additional properties
public class UserMessageLink {
[Key]
[Column("RecieverId", Order = 0)]
[ForeignKey("Reciever")]
public virtual int RecieverId { get; set; }
[Key]
[Column("MessageId", Order = 1)]
[ForeignKey("Message")]
public virtual int MessageId { get; set; }
public virtual User Reciever { get; set; }
public virtual Message Message { get; set; }
//This is an additional property
public bool IsRead { get; set; }
}

Categories

Resources