I've got the following EF model:
public class Order
{
public string Id { get; set; }
public IList<OrderLine> Lines { get; set; }
...
}
public class OrderLine
{
public string OrderId { get; set; }
public int Position { get; set; }
...
}
The primary key of OrderLine is made of OrderId and Position:
HasKey(l => new {l.Position, l.OrderId});
Everything worked well so far, until I added this new entity:
public class OrderLineReturn
{
public string Id { get; set; }
...
}
public class OrderLine
{
public string OrderId { get; set; }
public int Position { get; set; }
public IList<OrderLineReturn> LineReturns { get; set; }
...
}
And now I get an error when deleting orders:
DELETE instruction is in conflict with the REFERENCE constraint "FK_dbo.OrderLineReturn_dbo.OrderLine_OrderLine_Position_OrderLine_OrderId"
Could anyone help me understand the issue ?
I am thinking that you need to mark the ILIst property as virtual to enable Lazy loading. Also, I wonder if you need to make a Virtual Order Property in OrderLine instead of having the OrderID reference.
I'm not sure if I'm correct, but I think that you would have the same problem if you tried to delete an Order without deleting first it's OrderLines.
The problem is that OrderLineReturn will have a reference to the OrderLine, and when you delete the OrderLine without first deleting the OrderLineReturn it will give you that error.
I think you can enable cascade deleting...but it's always a risk.
Regards,
Related
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.
I'm using ASP.NET Core and EF Core and I have the two following parent and child classes. Each gift card can have many transactions:
public class GiftCard
{
public int Id { get; set; }
public string BarCode { get; set; }
public DateTime PurchaseDate { get; set; }
public string Comments { get; set; }
public byte[] Timestamp { get; set; }
public List<Transaction.Transaction> Transactions { get; set; }
}
public class Transaction
{
public int Id { get; set; }
public DateTime TransactionDate { get; set; }
public decimal TransactionAmount { get; set; }
public TransactionType TransactionType { get; set; }
public byte[] Timestamp { get; set; }
public GiftCard.GiftCard GiftCard { get; set; }
}
Based on what I read, this is the way to do it, by having navigation property on the parent and reference navigation in child. When I add my migrations and update the database using the command line, everything seemed ok in the database except that the GiftCardId foreign key in the Transactions table is nullable. I want to make sure this is NOT NULL. Am I missing a Data Annotation attribute?
Put the following property on your Transaction entity, and it should be resolved.
public int GiftCardId { get; set; }
What is happening with your definition is that a shadow property is being created and EF's Change Tracker is maintaining the relationships.
See here.
I am trying to create some tables using Code First. Here is my code:
public class Country
{
[Key]
public int Id { get; set; }
public string CountryName { get; set; }
}
public class State
{
[Key]
public int Id { get; set; }
public string StateName { get; set; }
public int CountryId { get; set; }
public Country Country { get; set; }
}
public class Customer
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int CountryId { get; set; }
public int StateId { get; set; }
public virtual Country Country { get; set; }
public virtual State State { get; set; }
}
public class ProductContext : DbContext
{
public DbSet<Country> Country { get; set; }
public DbSet<Customer> Customer { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
When I Execute this code the following error occurs:
Introducing FOREIGN KEY constraint
'FK_dbo.State_dbo.Country_CountryId' on table 'State' 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.
But I want the CountryId in State Table to be a foreign key. What am I missing here? Can anybody guide me the correct way of achieving this?
Entity Framework is worried about deletion here- because the User has a direct relationship to a Country and also a State and the State also relates to a Country you effectively have a potential loop of User -> State -> Country -> User which would mean that if cascade deletions were enabled the moment you deleted one user you would potentially delete everything in your database.
The answer is in the error message- by disabling cascade deletions across some of these relationships ( which is logical - deleting a user doesn't mean you want to delete their state and country ) you will avoid this risk. As you might imagine this has come up on SO before.
As an aside, having the Country on the User and also on the State looks like questionable denormalisation - there may be a good reason for it, but that happens less often than you would expect.
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; }
I have an app that was created using EF. The problem is that I noticed some extraneous foreign keys columns created in one of the tables. Dropping these columns causes an [SqlException (0x80131904): Invalid column name 'Material_Id' error.
Here is a simplified version of the class structure...
public class Hazard
{
public int Id { get; set; }
public string Name { get; set; }
}
public abstract class HazardAnalysis
{
public int Id { get; set; }
public int HazardId { get; set; }
public virtual Hazard Hazard { get; set; }
}
public class ProductHazard : HazardAnalysis
{
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
The table that was generated looked like this...
dbo.Hazards
Id int
Name string
Product_Id int
Since the relationship between ProductHazards and Hazards is 1:many, the Product_Id field should not be there. Dropping this column generates the Invalid column name 'Product_Id' error.
I've scoured the model for hours and can't find any valid reason for this column to exist.
Is there any way to update the model after manually dropping a column? I obviously don't want to drop and recreate the database.
I've also noticed that the productId of the current product is inserted in the dbo.Hazards Product_Id table whenever a new ProductHazard is created. Since there is a many-to-one relationship between ProductHazards and Hazards, when a new ProductHazard is created, the Product_Id field is updated with the ProductId of the new ProductHazard, which seems bizarre.
Any advice would be greatly appreciated.
Here is the DbSet code:
public DbSet<Hazard> Hazards { get; set; }
public DbSet<HazardAnalysis> HazardAnalyses { get; set; }
and also...
modelBuilder.Entity<HazardAnalysis>()
.HasRequired(e => e.Hazard)
.WithMany()
.HasForeignKey(e => e.HazardId)
.WillCascadeOnDelete(false);
You need to define the many part of the relationship. In this case, you need to add a collection property to your Hazard object, like below:
public class Hazard
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<HazardAnalysis> HazardAnalyses { get; set; }
}