ForeignKeyAttribute Not Working MVC3 EF5 - c#

I have these classes:
public class SystemRequirements : DbEntity
{
public string OS {get;set;}
}
public class Application : DbEntity
{
public string Name {get;set;}
public virtual SystemRequirements MinimumSystemRequirements {get;set;}
public Guid MinimumSystemRequirementsId {get;set;}
public virtual SystemRequirements RecommendedSystemRequirements {get;set;}
public Guid RecommendedSystemRequirementsId {get;set;}
}
I got an error saying: Introducting Foreign Key Constraint may cause cycles or multiple cascade paths.
DbEntity is an abstract class containing the Primary Key. [Key] public Guid Id {get;set;}
So, I changed Application to:
public class Application : DbEntity
{
public string Name {get;set;}
public virtual SystemRequirements MinimumSystemRequirements {get;set;}
[ForeignKey("MinimumSystemRequirements")]
public Guid MinimumSystemRequirementsId {get;set;}
public virtual SystemRequirements RecommendedSystemRequirements {get;set;}
[ForeignKey("RecommendedSystemRequirements")]
public Guid RecommendedSystemRequirementsId {get;set;}
}
So my question is why doesn't this work? I even tried putting a reference to Application in SystemRequirements, that didn't work?
Please don't trawl this post for spelling mistakes. My code is fine on VS, I copy and paste rather type out.

SystemRequirements probably needs to have the MinimumSystemRequirementsID and RecommendedSystemRequirmentsID fields. My understanding of EF FK relationships is it looks at the model for the value of the Element you are calling a FK and then uses that for linking.
Try this:
public class SystemRequirements : DbEntity
{
public string OS {get;set;}
public Guid MinimumSystemRequirementsId {get;set;}
public Guid RecommendedSystemRequirementsId {get;set;}
}

You have to tell EF that (at least) one of the associations between Application and SystemRequirements has no cascading delete, for instance:
modelBuilder.Entity<Application>().HasRequired(a => a.MinimumSystemRequirements)
.WithMany().HasForeignKey(a => a.MinimumSystemRequirementsId)
.WillCascadeOnDelete(false);
(In the context's overload of OnModelCreating).

Heyyyyy good news. I figured out the issue!
I remember back in the day (couple of months ago) I was told that in order to enable Cascade On Delete using Attributes in Code First you must do this:
public Guid CascadeOnId {get;set;}
public virtual Cascade CascadeOn {get;set;}
However before I knew that I was always using this:
public virtual Cascade CascadeOff {get;set;}
The reason I'm getting the cyclic delete, is because the first example will delete the HDD if it exists, but the HDD must always exist for the second instance of it, example:
public Guid FirstCascadeId {get;set;}
public virtual Cascade FirstCascade {get;set;}
public Guid SecondCascadeId {get;set;}
public virtual Cascade SecondCascade {get;set;}
So, entity framework is worrying that if you delete a cascade entity, it must delete it's associated parent, in deleting a parent you must delete all associated cascades. Which is where the cycle begins. I hope I'm making some sort of sense.
In order to turn Cascade On Delete off, you mustn't specify a Guid Id relating to that virtual. So In order to make the above work, you use:
public virtual Cascade FirstCascade {get;set;}
public virtual Cascade SecondCascade {get;set;}
You can turn on the first Cascade On Delete if you want, just make sure one of them hasn't got it and it works fine :)
I found the answer by using Model First, and then getting a massive glowing bulb above my head :P

Related

Entity Framework: Indexing a child entity based on a value on the parent entity for uniqueness

I have a quick question and looking for the best way to do this, whether EF has the capability or not, am not sure? I am using EntityFramework 6.3.
I have the following parent-child scenario,
public class Application{
[Key]
public int ApplicationId {get;set;}
public string Name {get;set;}
public string Status {get;set;}
public virtual List<Document> Documents {get;set;}
}
public class Document{
[Key]
public int DocumentId {get;set;}
[Index("IX_ApplicationDocument", 1, IsUnique = true)]
public string DocumentType {get;set;}
[Index("IX_ApplicationDocument", 1, IsUnique = true)]
public string Name {get;set;}
public int ApplicationId {get;set;}
[ForeignKey("ApplicationId")]
public Application Application {get;set;}
}
So an application is made to a department, and stored in the database, each application has a status and when submitted, status of pending, because various validation has to occur before it is approved. When an application is rejected, the submitter has to make a new application (please note I used a minimalistic example than what it actually is), however, the applicant may submit the same documents again. The problem is, this already exist in the system and can not be duplicated. As you can see, the second time they attempt to submit it will throw a constraint exception. How can I overcome this using EF, is there a way to create a constraint based on the parent's status, or is this something that can only be done programmatically?
Dont know if its helps you in your case or not, but check this out
Assuming your entity is defined as
public class Entity
{
public int Id { get; set; }
public int Parent { get; set; }
public int Child { get; set; }
}
Following fluent API code will create index as you desire:
modelBuilder.Entity<Entity>().HasIndex(p => new {p.Parent, p.Child})
.HasFilter("isdeleted = 0")
.HasName("unq_t_parent_child");
SQL generated
CREATE INDEX [unq_t_parent_child] ON [Entity] ([Parent], [Child]) WHERE isdeleted = 0;
HasIndex defines index over properties in the table Entity
HasFilter allows you to set a filter for your index. This value is sql so you need to make sure you are writing correct sql syntax.
HasName configures the name of the index.
(If you map Entity to table t & the properties to their column names, migrations will create exactly same sql as you want.)
Also a check constraint is different from unique index. If you are looking to add check constraint then you need to use migrationBuilder.Sql in your migration file.
So unfortunately I have searched around and there is no solution for this for EF 6. The best way I can do this was following the guidance of the following article, where you manually add the Filtered Index in your migration after table creation.
Blog

How to add a reference to a user in my entity in EF

I have the following classes (Simplified):
public class Company {
public Guid Id {get;set;}
public List<Farm> Farms {get;set;}
public List<User> Employees {get;set;}
}
public class Farm {
public Guid Id {get;set;}
public Company Owner {get;set;}
}
public class User {
public Guid Id {get;set;}
public string Name {get;set;}
public Guid CompanyId {get;set;}
public Company Company {get;set;}
}
This all works fine.
Now I want to add the User that created the Farm as follow:
public class Farm {
public Guid Id {get;set;}
public Company Owner {get;set;}
public User CreatedBy {get;set;} // <=
}
This gave me the following error (which is kind of obviouse I guess):
Introducing FOREIGN KEY constraint 'FK_dbo.Farms_dbo.Users_CreatedBy_Id' on table 'Farms' 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.
So I googled and found how to disable cascade deleting (which I hope means it won't delete my User once a Farm is being deleted). So I've added this:
modelBuilder.Entity<Farm>().HasRequired(f => f.CreatedBy).WithRequiredDependent().WillCascadeOnDelete(false);
After this I'm getting the Following error:
Entities in 'DbContext.Users' participate in the 'Farm_CreatedBy' relationship. 0 related 'Farm_CreatedBy_Source' were found. 1 'Farm_CreatedBy_Source' is expected.
I also noticed that not only the Farms-table has an User_Id-field but the Users-table now also has a Farm_Id-field.
Which is not what I expected/want. This looks like a 1-to-1 relation to me where every Farm has a User.
What must I do to configure this so:
My Farm has a reference to a User which will not be deleted on Cascade
My User doesn't contain a list with all the Farms he created (because I will probably want something for other entities to and I don't want to have lists for all those entities in my Model, unless that's the way to do it ofcourse :D)
*********** EDIT *************
I completely missed the List<User> on the Farm which was actually causing the 'weird' Foreign Key Farm_Id on the User.

EF Navigation Property in Database first

I have a database and I want to know how to map the relationships via code. I'm not sure if I understand exactly how this works.
Suppose I have two classes:
public class Address
{
[Key]
public int AddressID {get;set;}
public String Street {get;set;}
}
public class Shipment
{
[Key]
public int ShipmentID {get;set;}
public int ShipToAddressID {get;set;}
public virtual Address ShipToAddress {get;set;}
}
I have a few questions:
Does the navigation property merely give me access to the dbset of Address?
It seems that is not the case. However, if not, how do I specify which property is the foreign key on which the relationship exists? eg: How do I tell this navigation property that it should match the Address entities based on the AddressID property ?
Again, I'm doing this all via code. So I'm mapping the properties in the OnModelCreating call in the context. So please make suggestions/provide answers with that in mind.
You are in need of the HasRequired, WithMany, and HasForeignKey configuration methods.
EntityTypeConfiguration<Shipment> config = modelBuilder.Entity<Shipment>();
config
.HasRequired(s=>s.ShipToAddress)
.WithMany()
.HasForeignKey(s=>s.ShipToAddressID)
.WillCascadeOnDelete(false);

Entity Framework, strange behaviour with required reference, lazy loading

This goes for both Entity Framework 4 (4.3.1) and 5.
I have a User class (to go with my Entity Framework MembershipProvider). I've removed some of the properties to simplify. The actual User is from the MVCBootstrap project, so it's not part of the same assembly as the other classes.
public class User {
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[Required]
[StringLength(256)]
public String Username { get; set; }
}
And then I have this class:
public class NewsItem {
public Int32 Id { get; set; }
[Required]
[StringLength(100)]
public String Headline { get; set; }
[Required]
public virtual User Author { get; set; }
[Required]
public virtual User LastEditor { get; set; }
}
Then I create the database context (The DbSet for the user is in the MembershipDbContext):
public class MyContext : MVCBootstrap.EntityFramework.MembershipDbContext {
public MyContext(String connectString) : base(connectString) { }
public DbSet<NewsItem> NewsItems { get; set; }
}
Running this code will give me this exception when the database is being created:
Introducing FOREIGN KEY constraint 'FK_dbo.WebShop_dbo.User_LastEditor_Id' on table 'WebShop' 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. See previous errors.
So I change the database context:
public class MyContext : MVCBootstrap.EntityFramework.MembershipDbContext {
public MyContext(String connectString) : base(connectString) { }
public DbSet<NewsItem> NewsItems { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new NewsItemConfiguration());
}
}
And this configuration:
public class NewsItemConfiguration : EntityTypeConfiguration<NewsItem> {
public NewsItemConfiguration() {
HasRequired(n => n.Author).WithOptional();
HasRequired(n => n.LastEditor).WithOptional();
}
}
Or is this wrong?
Anyway, when I run the code, the database get's created, and the database seems okay (looking at foreign key constraints etc.).
But, then I get the 10 latest NewsItems from the context, and start loading them into view models, part of this is accessing the Author property on the NewsItem. The controller doing this takes forever to load, and fails after a long, long time. When running in debug mode, I get an exception in this piece of code: this.AuthorId = newsItem.Author.Id;, then exception I get is this:
A relationship multiplicity constraint violation occurred: An EntityReference can have no more than one related object, but the query returned more than one related object. This is a non-recoverable error.
It's probably something simple and stupid I'm doing wrong, I'm sure I've get similar code running on several sites, so .. what is causing this? Are my models wrong, is it the database context, or?
This part
Introducing FOREIGN KEY constraint 'FK_dbo.WebShop_dbo.User_LastEditor_Id' on table 'WebShop' 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. See previous errors.
is actually a SQL Server issue (and an issue of many other RDBMS's). It is a complex issue resolving multiple cascade paths, and SQL Server decides just to punt and not try. See
Foreign key constraint may cause cycles or multiple cascade paths?
You were trying to configure your model to delete the child Author and LastEditor objects when the NewsItem is deleted. SQL Server won't do that.
Come to think of it... is that what you want? It seems you would want to disassociate the Author and LastEditor from the NewsItem, not delete them from the database.
Your object model requires a 1:1 relationship between NewsItem and Author, and between NewsItem and LastEditor. I'm not sure what this refers to in the code
this.AuthorId = newsItem.Author.Id;
but it seems to me, you should be making the assignment the other way around, e.g.
newsItem.Author = myAuthorInstance;
or if you include foreign key properties in your model and if you have previously saved your author instance and have an Id:
newsItem.AuthorId = myAuthorInstance.Id;
If you share the generated DB schema (relevant parts) that would make it easier to diagnose the issue.
User can be an author of several news items. Also, user can be editor of several news items.
Hence, relationship have to be "one-to-many":
public class NewsItemConfiguration : EntityTypeConfiguration<NewsItem> {
public NewsItemConfiguration() {
HasRequired(n => n.Author).WithMany();
HasRequired(n => n.LastEditor).WithMany();
}
}

Cyclical reference error in MVC3 & Entity Framework 4

I admit using MVC3/EF4 has been a breeze and the context:models work better then I'd hoped (though I'm always leary of constructs/frameworks that make things happen behind some curtain, but hey I'm old school grey beard), and all went well until I hit this issue.
I've seen many postings related to this issue but don't know how to solve it in my case. Each table(entity) has an employeeID field and it can (and usually is) a different employee for most records in each table. Apparently only one table in the DbContext can have a 'virtual employee employee' (or whatever I choose to name it) defined or else I get the dreaded "cyclical reference error". Ideally I'd like all three tables to have it so I can easily access the employees name. I may have to override the OnModelCreating() but that's (fluent API) totally out of my league. Any EF gurus out there????
Models:
class meeting{
int meetingID; //key
string description {get;set;}
int employeeID {get;set;} // who scheduled
public virtual employee employee {get;set;}
public virtual ICollection<agenda> agendas {get;set;}
}
class agenda{
int agendaID {get;set;} // key
int employeeID {get;set;} // initiator
public virtual employee employee {get;set;}
public virtual ICollection<actionItem> actionItems {get;set;}
}
class actionItem{
int actioItemID {get;set;} //key
string description {get;set;}
int employeeID {get;set;} // action item lead
public virtual employee employee {get;set;}
}
class employee{
int employeeID {get;set;}//key
string name {get;set;}
}
context:
public class meetings:DbContext{
public DbSet<meeting> meetings {get;set;}
public DbSet<agenda> Agendas
public DbSet<actionItem> actionItems{get;set;}
}
I assume you're getting this error on serialization of your models. Most people use a second set of models to abstract from the EF models, but if you want to stick with those, use the approach from this answer to get rid of your cyclical reference problem:
EF 4.1 - Code First - JSON Circular Reference Serialization Error

Categories