EF 7 : INSERT statement conflicted with the FOREIGN KEY SAME TABLE - c#

I have an exception when I call SaveChangesAsync.
My architecture is really simple, i have a Category class, wich contains
public class Category {
public Guid ID {get; set;}
public Guid? ParentId {get; set;}
public Category Parent {get; set;}
[...]
}
When I want to insert a new category in database (connected to my ASP MVC application), I set the GUID before doing the insert.
The error occured when my database is empty and I want to insert parent category (so with a null Guid IdParent and null Parent). This is NOT happening if I set a parent value. I can add a record manually by setting parent to Null by Visual studio.
I have the following error :
The INSERT statement conflicted with the FOREIGN KEY SAME TABLE
constraint "FK_Category_Category_ParentId". The conflict occurred in
database "HT_Root", table "dbo.Category", column 'ID'. The statement
has been terminated.
I searched on stack overflow for a simple answer, and not found it.
I try with Fluent API :
modelBuilder.Entity<Category>().HasOne(s => s.Parent).WithMany().HasForeignKey(s => s.ParentId);
But nothing changed. What am I doing wrong ?

It seems to have changed in EF 7 See this github issue
Try
public class Category
{
public Guid ID {get; set;}
public Guid? ParentId {get; set;}
public Category Parent {get; set;}
public ICollection<Categories> Children {get; set;}
}
And
modelBuilder.Entity<Category>()
.HasOne(x => x.Parent)
.WithMany(x => x.Children)
.HasForeignKey(x => x.ParentId)
.Required(false);
You should also always check that (if specified) the ParentId exists in the database. Watch out for adding Guid.Empty (00000000-0000-0000-0000-000000000000) instead of null as this can cause issues.

Related

Entity Framework Map With Fake Foreign Key Property

My models:
class FooEntity
{
[Key]
int Id { get; set; }
[ForeignKey("Bar"), Column("other_id")]
int OtherId { get; set; } // <-- This should be the FK
virtual BarEntity Bar { get; set; }
}
class BarEntity
{
[Key]
int Id { get; set; }
[Column("other_id")]
int OtherId { get; set; } // <-- This is the other side of the FK
}
Essentially I want to reproduce this SQL:
select *
from foo f
left join bar b on b.other_id = f.other_id -- AND other conditions to "guarantee" uniqueness
But with the model building configuration:
modelBuilder.Entity<FooEntity>()
.HasOptional(f => f.Bar)
.WithRequired()
.Map(m => m.MapKey("other_id"))
.WillCascadeOnDelete(false);
I end up with the error: "Each property name in a type must be unique. Property name 'other_id' is already defined." But when I add:
modelBuilder.Entity<BarEntity>().Ignore(b => b.OtherId);
before the other configuration, I get the error: "the specified type member 'OtherId' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported."
So how can I get this working? Changing the underlying data structure is absolutely not an option.
In EF6 an FK must point back to a PK.
In your RDBMS it could depend on the implementation. Sql Server will allow an FK back to a non PK column as long as there is a unique index constraint. Other RDBMS might or might not allow it.
I recommend you omit the FK relationship and when you need to retrieve both entities manually include the join in your Linq/Lambda statement.
There's no shortcut for what you want to accomplish; you will need to create the query manually, something along the lines of:
dbContext.Foos
.GroupJoin(
dbContext.Bars,
f => f.OtherId,
b => b.OtherId,
( foo, bars ) => new { foo, bars } )

Custom name/type of document ID and performance

I need to store a class that looks like this
public class Wsp{
//mapped
public ObjectId ObjId {get; set;} // not called Id
public Guid Id {get; set;}
public string Name {get; set;}
//NOT mapped
public string Size {get; set;}
public List<string> Sit {get; set;}
.....
}
BsonClassMap.RegisterClassMap<Wsp>(map =>
{
map.MapProperty(p => p.Name);
map.MapProperty(p => p.ID);
//what to do with ObjId ??
});
I want to store only 3 values in Wsp Document in mongoDb.
ObjId -> primary key
Id
Name
When I select object from DB i want these values to be populated.
Another question is if in another class can I use Guid property as a primary key ? Will performance be worst than with ObjectID ? Can mongo db generate Guid or I must provide unique guid before inserting ?
First question, just add BsonId like:
[BsonId]
public ObjectId ObjId {get; set;} // not called Id
Which indicates that this filed is primary key and should be mapped to _id field.
Basically you can use anything as ID, there's no limitation you must use some specified type. Thus you can use int/GUID/string, anything, as long as they are unique. You must provide ID if you expect it to be anything other than ObjectId. Actually ObjectId is generated by driver, not by MongoDB.
EDIT: When you are using BsonClassMap, try the following
BsonClassMap.RegisterClassMap<Wsp>(map =>
{
map.AutoMap();
map.MapProperty(p => p.Name);
map.MapProperty(p => p.ID);
});
The document explained it clearly:
For each convention there is a default convention that is the most likely one you will be using, but you can override individual conventions (and even write your own) as necessary.

Entity Framework: How to enable cascade delete on one way related entities

There are Two Entities such as bellow:
public class Business
{
public int Id {get; set;}
public File Logo {get; set;}
public int? LogoId {get; set;}
public File Video {get; set;}
public int? Video {get; set;}
public ICollection<File> Images {get; set;}
}
public class File
{
// some file props, such as Id, Name, ...
}
How can I configure cascade delete for files on business delete?
Please consider that I don't need any navigation from File to Business.
UPDATE:
You may find the modelBuilder configuration as bellow:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Entities.Business>()
.HasOptional(b => b.Logo)
.WithOptionalPrincipal()
.WillCascadeOnDelete();
modelBuilder.Entity<Entities.Business>()
.HasOptional(b => b.Video)
.WithOptionalPrincipal()
.WillCascadeOnDelete();
modelBuilder.Entity<Entities.Business>()
.HasMany(b => b.Images)
.WithOptional()
.WillCascadeOnDelete();
and here is the error I've got:
Introducing FOREIGN KEY constraint
'FK_dbo.Files_dbo.Businesses_Business_Id1' on table 'Files' 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
If you like to use seperate configuration classes you could try something like that:
public class BusinessConfiguration : EntityTypeConfiguration<Business>
{
public BusinessConfiguration()
{
HasMany(x => x.Images).WithOptional().WillCascadeOnDelete();
HasOptional(x => x.Logo).WithOptional().WillCascadeOnDelete();
HasOptional(x => x.Video).WithOptional().WillCascadeOnDelete();
}
}
When you do not pass a lambda within .WithOptional() or .WithRequired() means the other side has no navigation properties.

EF Code First: Many-to-Many + Additional Property

I have the following tables: Country, Language and Country2Language
Country2Language is the linking table that creates the M:M relationship which is defined using Fluent Mapping:
mb.Entity<Country>()
.HasMany(e => e.Languages)
.WithMany(set => set.Countries)
.Map(mc =>
{
mc.ToTable("Country2Language");
mc.MapLeftKey("CountryShortName");
mc.MapRightKey("LanguagesID");
}
);
My question: How can I add an additional DateTime "DateCreated" property?
Would it work to create the table with the created column and then set the default value in the database to the GetTime function (or w/e it is)? Then just use the left/right mapping and let the db automatically handle the default value for created?
I've had issues in the past where EF wants to insert null into columns if you don't specify the column is a db generated value, but the fact this is for a many-to-many relationship it probably wouldn't even know it was there and should always let it become the default.
You have to create an entity for your mapping that maps to the table.
public class Country2Language {
[Key]
[Column(Order = 0)]
[ForeignKey("CountryShortName")]
public int CountryShortname{ get; set; }
[Key]
[Column(Order = 1)]
[ForeignKey("LanguagesID")]
public int LanguagesID { get; set; }
public DateTime DateCreated {get; set;}
}

nHibernate ManyToManyToMany with Fluent

I have a structure here I have a ManyToManyToMany relationship.
This is my (truncated) fluent mappings.
public AgencyMap()
{
Id(x => x.AgencyId, "AgencyId");
Map(x => x.AgencyCode, "AgencyCode");
HasManyToMany<Personnel>(x => x.Personnel)
.WithTableName("AgencyPersonnel")
.WithParentKeyColumn("AgencyId")
.WithChildKeyColumn("PersonnelId").Cascade.All().LazyLoad();
}
public PersonnelMap()
{
Id(x => x.PersonnelId, "PersonnelId");
HasManyToMany<Discipline>(x => x.Disciplines)
.WithTableName("AgencyPersonnelDiscipline")
.WithParentKeyColumn("AgencyPersonnelId")
.WithChildKeyColumn("DisciplineId")
.Cascade.SaveUpdate().LazyLoad();
}
public DisciplineMap()
{
SchemaIs("dbo");
Id(x => x.DisciplineId, "DisciplineId");
Map(x => x.DisciplineCode, "DisciplineCode");
Map(x => x.Description, "Description");
}
If I then run code like this.
Agency agency = m_AgencyRepository.Get(10);
var personnel = new Personnel() { ... };
personnel.Disciplines.Add(new Discipline() { ... });
agency.Personnel.Add(personnel);
m_AgencyRepository.Save(agency);
When I run this code I get this error.
could not insert collection: [PPSS.Model.Personnel.Disciplines#22][SQL: SQL not available]
The INSERT statement conflicted with the FOREIGN KEY constraint "AgencyPersonnel_AgencyPersonnelDiscipline_FK1". The conflict occurred in database "PPSS2", table "dbo.AgencyPersonnel", column 'AgencyPersonnelId'.\r\nThe statement has been terminated.
Enitities look like.
public class Agency
{
public virtual int AgencyId {get; set;}
public virtual IList<Personnel> Personnel {get; set;}
}
public class Personnel
{
public virtual int PersonnelId {get; set;}
public virtual IList<Agency> Agencies {get; set;}
public virtual IList<Dependency> Dependencies {get; set;}
}
public class Dependency
{
public virtual int DependencyId {get; set;}
public virtual string Name {get; set;}
}
If I add an inverse to the ManyToMany in personnel then the Personnel is saved but not the disciplines (no exception is raised).
How should this mapping be done?
Edit:
I have got some more info. If I disable the constraint AgencyPersonnel_AgencyPersonnelDiscipline_FK1 then it all is inserted ok. This makes me think it is the order nHibernate is inserting that is the problem. What I would expect it to do in order is.
INSERT INTO Personnel
Get last key (PersonnelId)
INSERT INTO AgencyPersonnelDiscipline with AgencyId and PersonnelId.
This would not violate any constraints.
Since this is a truncated mapping I'm gonna take a shot in the dark :). This kind on of exception usually happens to me when I have 2 entities : X and Y , and I map them from both directions, something like :
X has a property Y
Y has a list of X
And none of them has the Inverse() attribute set. Check if you're mapping something in both directions but not setting one on them Inverse().
Given that the maps you have are correct for the types you state, you haven't mapped back from Personnel to Agency or from Discipline to Personnel.

Categories