Updating a navigation property that is a relationship to the same type? - entity-framework

I am using code first and have several classes that have navigation properties between themselves.
Issue Class:
public class Issue
{
public Issue()
{
Complaints = new List<Complaint>();
SubIssues = new List<Issue>();
}
[Key,ForeignKey("Complaints")]
public int IssueID { get; set; }
public string Name { get; set; }
public bool IsSubCategory { get; set; }
public virtual Issue ParentIssue { get; set; }
public virtual ICollection<Issue> SubIssues { get; set; }
public virtual ICollection<Complaint> Complaints { get; set; }
}
The Complaint Class:
public class Complaint
{
public Complaint()
{
CreateDate = DateTime.Now;
}
public int ComplaintID { get; set; }
public DateTime CreateDate { get; set; }
[MaxLength(2000)]
public string Description { get; set; }
public bool IsClosed { get; set; }
[ForeignKey("IssueID")]
public virtual Issue Issue { get; set; }
public int IssueID { get; set; }
}
The Complaint class is working fine. Where I am running into difficulties is with the Issues class which references the same table for SubIssues and ParentIssue. The idea is that each Issue record with IsSubCategory == false can have 0 to many related Issue records as a collection of SubIssues and each Issue record with IsSubCategory == true will have a 1 to 1 relationship with an Issue record as ParentIssue.
Because of some DBA standards I also need to specify the naming of the Foreign key fields, i.e. ParentIssueID rather than the Issue_ParentIssueID (or whatever it auto gens)
I would prefer to do this with data annotations but could use the OnModelCreating process if need be.
How would I go about fixing the issue class so that the proper tables are created?

IssueID can't be both a primary and a foreign key to itself. You need a property (and field) ParentIssueId.
public int? ParentIssueID { get; set; }
The mapping, if using fluent mapping, should look like this:
modelBuilder.Entity<Issue>()
.HasMany(i => i.SubIssues)
.WithOptional(i => i.ParentIssue)
.HasForeignKey(i => i.ParentIssueID);
ParentIssueID is int? because it's an optional relationship.

Related

Defining Self Referencing Foreign-Key-Relationship Using Entity Framework 7 Code First

I have an ArticleComment entity as you can see below:
public class ArticleComment
{
public int ArticleCommentId { get; set; }
public int? ArticleCommentParentId { get; set; }
//[ForeignKey("ArticleCommentParentId")]
public virtual ArticleComment Comment { get; set; }
public DateTime ArticleDateCreated { get; set; }
public string ArticleCommentName { get; set; }
public string ArticleCommentEmail { get; set; }
public string ArticleCommentWebSite { get; set; }
public string AricleCommentBody { get; set; }
//[ForeignKey("UserIDfk")]
public virtual ApplicationUser ApplicationUser { get; set; }
public Guid? UserIDfk { get; set; }
public int ArticleIDfk { get; set; }
//[ForeignKey("ArticleIDfk")]
public virtual Article Article { get; set; }
}
I want to define a foreign key relationship in such a way that one comment can have many reply or child, I've tried to create the relationship using fluent API like this:
builder.Entity<ArticleComment>()
.HasOne(p => p.Comment)
.WithMany()
.HasForeignKey(p => p.ArticleCommentParentId)
.OnDelete(DeleteBehavior.Restrict)
.IsRequired(false);
I followed the solution that was proposed here and here, but I get an error with the message:
Introducing FOREIGN KEY constraint 'FK_ArticleComment_ArticleComment_ArticleCommentParentId' on table 'ArticleComment' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
First I though by setting the OnDelete(DeleteBehavior.Restrict) this would go away, but the problem persist, also I've tried to use the data annotation [ForeignKey("ArticleCommentParentId")] as you can see the commented code in the ArticleComment definition, but it didn't work, I'd appreciate any though on this.
You are not modeling correctly your entity. Each comment needs a Set of replies, which are of type ArticleComment too, and each of those replies are the ones that point back to its parent (Note the added ICollection Replies property):
public class ArticleComment
{
public ArticleComment()
{
Replies = new HashSet<ArticleComment>();
}
public int ArticleCommentId { get; set; }
public int? ParentArticleCommentId { get; set; }
public virtual ArticleComment ParentArticleComment{ get; set; }
public virtual ICollection<ArticleComment> Replies { get; set; }
//The rest of the properties omitted for clarity...
}
...and the fluent Mapping:
modelBuilder.Entity<ArticleComment>(entity =>
{
entity
.HasMany(e => e.Replies )
.WithOne(e => e.ParentArticleComment) //Each comment from Replies points back to its parent
.HasForeignKey(e => e.ParentArticleCommentId );
});
With the above setup you get an open-ended tree structure.
EDIT:
Using attributes you just need to decorate ParentArticleComment property.
Take into account that in this case EF will resolve all the relations by convention.
[ForeignKey("ParentArticleCommentId")]
public virtual ArticleComment ParentArticleComment{ get; set; }
For collection properties EF is intelligent enough to understand the relation.
I simplified the class (removing foreign key support fields) and it works.
It could be an issue of your EF version (I've just installed it but actually I think I'm using rc1 but I'm not sure because I had several dependency issues) or it could be your model.
Anyway, this source works fine
public class ArticleComment
{
public int ArticleCommentId { get; set; }
public virtual ArticleComment Comment { get; set; }
public DateTime ArticleDateCreated { get; set; }
public string ArticleCommentName { get; set; }
public string ArticleCommentEmail { get; set; }
public string ArticleCommentWebSite { get; set; }
public string AricleCommentBody { get; set; }
}
class Context : DbContext
{
public Context(DbContextOptions dbContextOptions) : base(dbContextOptions)
{}
public DbSet<ArticleComment> Comments { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ArticleComment>()
.HasOne(p => p.Comment)
.WithMany();
}
}
static class SampleData
{
public static void Initialize(Context context)
{
if (!context.Comments.Any())
{
var comment1 = new ArticleComment()
{
AricleCommentBody = "Article 1"
};
var comment2 = new ArticleComment()
{
AricleCommentBody = "Article 2 that referes to 1",
Comment = comment1
};
context.Comments.Add(comment2);
context.Comments.Add(comment1);
context.SaveChanges();
}
}
}

EF Code-First creates some fields ,even I added ForeignKey annotations

can any one help me in this ?
Here is my 2 classes
class Request
{
public Nullable<int> BuyCurrencyId {get ; set;}
public Nullable<int> SaleCurrencyId {get ; set;}
[ForeignKey("SaleCurrencyId")]
public virtual Currency SaleCurrency { get; set; }
[ForeignKey("BuyCurrencyId")]
public virtual Currency BuyCurrency { get; set; }
}
class Currency
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Request> Requests { get; set; }
public virtual ICollection<Request> Requests1 { get; set; }
}
I checked the updated with EF database , and I found out that the EF create Reqyests table like this :
SaleCurrencyId int (Already exists)
BuyCurrencyId int (Already exists)
Currency_Id int (Added by EF)
Currency_Id1 int (Added by EF)
By this not thing I expect. I thing the last tow columns are not correct and they not be exist.
Can any one help me ?
I am using EF 6 alpha to update the existing database with my generated model by T4.Please keep it in mind that I want to use data annotations , not Fluent API
Sorry about my bad English
Update 1 :
I thought if I change the Currency class to this it will resolve my problem , but it did not.
class Currency
{
public int Id { get; set; }
public string Name { get; set; }
[InverseProperty("SaleCurrencyId")]
public virtual ICollection<Request> Requests { get; set; }
[InverseProperty("BuyCurrencyId")]
public virtual ICollection<Request> Requests1 { get; set; }
}
Your Update1 is almost the correct solution, but the parameter of the [InverseProperty] attribute must be the navigation property in Request, not the foreign key property:
[InverseProperty("SaleCurrency")]
public virtual ICollection<Request> Requests { get; set; }
[InverseProperty("BuyCurrency")]
public virtual ICollection<Request> Requests1 { get; set; }

breeze.js one to many

I'm currently building an SPA with Web API and knockout etc. So far i worte my own simple datacontext and it worked pretty well.
The I bumped in to breeze and thought it might be worth a try. especially I hoped to get a simpler approach on navigation between the entities...
to load a entities or a single entity with breeze worked fine. Working with navigation properties seems not to work. The navigation property is always empty, even though it's a one to many relationship.
Here is my model (simplified):
public class WorkdayHours
{
public int Id { get; set; }
public bool IsWorkDay { get; set; }
...
public Byte WeekDay { get; set; }
}
public class Service
{
public int Id { get; set; }
public string DisplayName { get; set; }
public virtual ICollection<WorkdayHours> BookableDays { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string DisplayName { get; set; }
public virtual ICollection<WorkdayHours> BookableDays { get; set; }
}
public class Shop
{
public int Id { get; set; }
public string DisplayName { get; set; }
public virtual ICollection<WorkdayHours> BookableDays { get; set; }
}
Then I fetch the entity service ind my SPA as follow:
var query = EntityQuery
.from('Services')
.where('id', 'eq', serviceId)
.expand('BookableDays');
As when teh query is executed I get as result the requested service entity with all the data except the bookableDay property is always an empty array.
When I check the Json answer I see that also the workdayHours are transmitted and breeze even calls my defined ctors for this entities. However they are not linked to the bookableDays property itself.
When checking the genrated DB model, EF generated foreignkeys for service, employee and shop in workdayHours as expected.
Is breeze not capable with having several optional foreignkeys?
Suggestion and ideas highly apprechiated.
Breeze is dependent on Foreign Keys. I had a similar problem. This should solve it:
EF was generating the ForeignKeys for me too and the related Entites where still empty. As far as i know breeze needs the explicit Annotation/Configuration of ForeignKey Fields.
public class Mvl
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long MvlId{ get; set; }
public DateTime CreatedAt { get; set; }
[InverseProperty("Mvl")]
public ICollection<MvlOP> MvlOps { get; set; }
public DateTime? ReleasedAt { get; set; }
public DateTime? LockedAt { get; set; }
public DateTime? ClosedAt { get; set; }
//[ConcurrencyCheck]
//public int? RowVersion { get; set; }
[Timestamp]
public byte[] TimeStamp { get; set; }
}
public class MvlOP
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long MvlOpId { get; set; }
public long MvlId { get; set; }
[ForeignKey("MvlId")]
public Mvl Mvl { get; set; }
...
}

Why am I getting an extra foreign key column with Entity Framework Code First Foreign Key Attributes?

I recently came across this strange problem with Entity Framework Code First.
My class looks like this
public class Status
{
[Key]
public int StatusID { get; set; }
public string Name { get; set; }
public int MemberID { get; set; }
[ForeignKey("MemberID")]
public virtual Member Member { get; set; }
public int PosterID { get; set; }
[ForeignKey("PosterID")]
public virtual Member Poster { get; set; }
public virtual ICollection<StatusLike> StatusLikes { get; set; }
public virtual ICollection<StatusComment> StatusComments { get; set; }
}
My Member class looks like this
public class Member
{
[Key]
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Bio { get; set; }
public virtual ICollection<MemberCourseTaken> MemberCourseTakens { get; set; }
public virtual ICollection<Status> Statuses { get; set; }
public virtual ICollection<Club> FoundedClubs { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public string Phone { get; set; }
public int AccountSourceID { get; set; }
public AccountSource AccountSource { get; set; }
public int AddressID { get; set; }
public Address Address { get; set; }
public string ProfilePhoto { get; set; }
public int MemberRankID { get; set; }
public MemberRank MemberRank { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
}
And for whatever reason the database table that is created has the following columns
StatusID
Name
MemberID
PosterID
Member_MemberID
with MemberID, PosterID, and Member_MemberID being foreign keys.
How can I keep Member_MemberID from being generated?
Your Member_MemberID column is created because of the Member.Statuses property. I can imagine that this is not what you want. Probably members and statuses should exist independent of each other, so you need a junction table.
I don't know if you already use the OnModelCreating override of the DbContext, but that's the place to change the mapping between Member and Status:
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<Member>().HasMany(m => m.Statuses).WithMany();
}
This will create a table MemberStatuses table with the two Id columns as foreign keys. This is a way to model a many-to-many relationship without a navigation property on the "other" side of the association. (I don't think you want a Members property in Status).
I've seen this before. In my case (Using EF 6.1), it was because my Fluent API Mapping was set up like so:
// In my EntityTypeConfiguration<Status>
HasRequired(x => x.Member).WithMany().HasForeignKey(x => x.MemberID);
That code works perfectly fine, but it doesn't tell EF that my Member class's Collection Navigational Property Status ha been taken into account. So, while I explicitly handled the existence of a Member Navigational Property in my Status Class, I now left an orphaned related collection property. That orphaned property, being a collection, tells EF that my Status class needs to have a Foreign Key to it. So it creates that on the Status Class.
To fix it, I had to be 100% explicit.
HasRequired(x => x.Member).WithMany(x => x.Statuses).HasForeignKey(x => x.MemberID)
It could bee that your Statuses Collection property in Member needs an attribute telling it that it is already considered, and not to go auto-creating mappings. I don't know that attribute.

How do I code an optional one-to-one relationship in EF 4.1 code first with lazy loading and the same primary key on both tables?

I'm working with an application and data structure built upon ASP/ADO.NET and I'm converting part of it to ASP.NET MVC. In the data structure, there exists a "optional one-to-one" relationship, where both tables use the same primary key, and name. Basically this table can be considered an "optional extension" of the primary table. Here are samples of the model:
public class ZoneMedia
{
public int ZoneMediaID { get; set; }
public string MediaName { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public virtual ZoneMediaText MediaText { get; set; }
}
public class ZoneMediaText
{
public int ZoneMediaID { get; set; }
public string Text { get; set; }
public int Color { get; set; }
}
Obviously, EF 4.1 code first has an issue mapping this automatically. So I realize I must specify the mapping explicitly. I tried this:
modelBuilder.Entity<ZoneMedia>()
.HasOptional(zm => zm.ZoneMediaText);
modelBuilder.Entity<ZoneMediaText>()
.HasRequired(zmt => zmt.ZoneMedia)
.WithRequiredDependent(zm => zm.ZoneMediaText)
.Map(m => m.MapKey("ZoneMediaID"));
But it is still giving me an exception about the name of the primary key.
Schema specified is not valid. Errors:
(199,6) : error 0019: Each property name in a type must be unique. Property name 'ZoneMediaID' was already defined.
I'm a little stumped. I need to adapt to this non-conventional structure I realize in EF 4.1 it would be much easier to just add a unique PK to the optional relation and hold the foreign key relationship in the primary table, but I can't change the database layout. Any advice would be appreciated.
I hope i understood well.
This works for me:
public class ZoneMedia
{
public int ZoneMediaID { get; set; }
public string MediaName { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public virtual ZoneMediaText MediaText { get; set; }
}
public class ZoneMediaText
{
public int ZoneMediaID { get; set; }
public string Text { get; set; }
public int Color { get; set; }
public virtual ZoneMedia ZoneMedia { get; set; }
}
public class TestEFDbContext : DbContext
{
public DbSet<ZoneMedia> ZoneMedia { get; set; }
public DbSet<ZoneMediaText> ZoneMediaText { get; set; }
protected override void OnModelCreating (DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ZoneMedia>()
.HasOptional(zm => zm.MediaText);
modelBuilder.Entity<ZoneMediaText>()
.HasKey(zmt => zmt.ZoneMediaID);
modelBuilder.Entity<ZoneMediaText>()
.HasRequired(zmt => zmt.ZoneMedia)
.WithRequiredDependent(zm => zm.MediaText);
base.OnModelCreating(modelBuilder);
}
}
class Program
{
static void Main (string[] args)
{
var dbcontext = new TestEFDbContext();
var medias = dbcontext.ZoneMedia.ToList();
}
}
This Correctly create a FK_ZoneMediaTexts_ZoneMedias_ZoneMediaID in ZomeMediaTexts table, and the Foreign Key is the Primary Key.
EDIT: maybe it's worth pointing out that I'm using EF 4.3.0