EF Code First Many-to-Many not working - c#

Using the Entity Framework Code First paradigm I have defined the following objects and relationships as part of an ASP.Net MVC3 application. The problem is the many-to-many relationship between the Photo and Gallery objects is not working properly.
Currently a gallery can contain many photos - this is correct. But a photo can only be associated with one Gallery - this is incorrect. I want a photo to be able to be in many galleries. If I add a photo to a gallery when it is already associated with another gallery, it is removed from the other gallery.
I would very much appreciate your solutions on how to make this many-to-many relationship work.
Here's my code:
public class Photo
{
public int ID { get; set; }
public string Title { get; set; }
public virtual ICollection<Gallery> Galleries { get; set; }
}
public class Gallery
{
public int ID { get; set; }
public string Title { get; set; }
public virtual ICollection<Photo> Photos { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
/* I would expect the following to cause a lookup table to
* be created but it isnt */
modelBuilder.Entity<Photo>().HasMany<Gallery>(p => p.Galleries);
modelBuilder.Entity<Gallery>().HasMany<Photo>(g => g.Photos);
}
I am using the following code within my own custom database initializer.
public void InitializeDatabase(PhotoDBContext context)
{
var dbCreationScript = ((IObjectContextAdapter)context).ObjectContext.CreateDatabaseScript();
Log(dbCreationScript);
context.Database.ExecuteSqlCommand(dbCreationScript);
}
By logging the dbCreationScript variable I can see that the relevant sql that is generated for these tables is as follows.
create table [dbo].[Galleries] (
[ID] [int] not null identity,
[Title] [nvarchar](max) null,
[Photo_ID] [int] null,
primary key ([ID])
);
create table [dbo].[Photos] (
[ID] [int] not null identity,
[Title] [nvarchar](max) null,
[Gallery_ID] [int] null,
primary key ([ID])
);
alter table [dbo].[Photos] add constraint [Gallery_Photos] foreign key ([Gallery_ID]) references [dbo].[Galleries]([ID]);
alter table [dbo].[Galleries] add constraint [Photo_Galleries] foreign key ([Photo_ID]) references [dbo].[Photos]([ID]);
As you can see there is no SQL generated to create a lookup table which is probably why the relationship is not working - why isnt a lookup table being created?

I believe you need to use something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Photo>()
.HasMany<Gallery>(x => x.Galleries)
.WithMany(x => x.Photos)
.Map(x =>
{
x.MapLeftKey("ID");
x.MapRightKey("ID");
x.ToTable("GalleryPhotos");
});
}

Related

One-to-one becomes one-to-many Entity Framework generating database-first

I'm generating Entity Framework database first (EF Designer from database). I've got two tables in Microsoft SQL Server:
CREATE TABLE dbo.Person
(
Pk_Person_Id INT IDENTITY PRIMARY KEY,
Name VARCHAR(255),
EmailId VARCHAR(255),
);
CREATE TABLE dbo.PassportDetails
(
Pk_Passport_Id INT PRIMARY KEY,
Passport_Number VARCHAR(255),
Fk_Person_Id INT UNIQUE
FOREIGN KEY REFERENCES dbo.Person(Pk_Person_Id)
);
INSERT INTO dbo.Person
VALUES ('Niraj','v.a#emails.com'),
('Vishwanath','v.v#emails.com'),
('Chetan','c.v#emails.com');
GO
INSERT INTO dbo.PassportDetails
VALUES (101, 'C3031R33', 1), (102, 'VRDK5695', 2), (103, 'A4DEK33D', 3);
GO
SELECT * FROM dbo.Person
SELECT * FROM dbo.PassportDetails;
In SQL Server the relations are shown as one-to-one because Fk_Person_Id is set as isUnique = true:
Fk_Person_Id INT UNIQUE
FOREIGN KEY REFERENCES dbo.Person(Pk_Person_Id));
Now in Visual Studio, I add a new ADO.NET Entity Data Model -> EF Designer from database -> select these two tables and leave all checkbox options by default.
Then after generation is over I see this in Visual Studio diagram .edmx:
And the relationship has changed for one-to-many - why? Is this wrong? I don't want a person to have a collection of passports - it's not the logic I'm trying to describe.
And the EF code:
public partial class Person
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Person()
{
this.PassportDetails = new HashSet<PassportDetail>();
}
public int Pk_Person_Id { get; set; }
public string Name { get; set; }
public string EmailId { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<PassportDetail> PassportDetails { get; set; }
}
public partial class PassportDetail
{
public int Pk_Passport_Id { get; set; }
public string Passport_Number { get; set; }
public Nullable<int> Fk_Person_Id { get; set; }
public virtual Person Person { get; set; }
}
Context
public partial class LightCRMEntities : DbContext
{
public LightCRMEntities() : base("name=LightCRMEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<PassportDetail> PassportDetails { get; set; }
public virtual DbSet<Person> People { get; set; }
}
Well, in database model editor (.edmx file editor), by clicking on a relationship (arrow between entities daigrams), you can configure its properties in such way, to make it 1-to-1 (it won't change your database!), but then you can generate the code you need to create such database. In result, I got the following:
-- --------------------------------------------------
-- Creating all tables
-- --------------------------------------------------
-- Creating table 'PassportDetails'
CREATE TABLE [dbo].[PassportDetails] (
[Pk_Passport_Id] int NOT NULL,
[Passport_Number] varchar(255) NULL
);
GO
-- Creating table 'Person'
CREATE TABLE [dbo].[Person] (
[Pk_Person_Id] int IDENTITY(1,1) NOT NULL,
[Name] varchar(255) NULL,
[EmailId] varchar(255) NULL
);
GO
-- --------------------------------------------------
-- Creating all PRIMARY KEY constraints
-- --------------------------------------------------
-- Creating primary key on [Pk_Passport_Id] in table 'PassportDetails'
ALTER TABLE [dbo].[PassportDetails]
ADD CONSTRAINT [PK_PassportDetails]
PRIMARY KEY CLUSTERED ([Pk_Passport_Id] ASC);
GO
-- Creating primary key on [Pk_Person_Id] in table 'Person'
ALTER TABLE [dbo].[Person]
ADD CONSTRAINT [PK_Person]
PRIMARY KEY CLUSTERED ([Pk_Person_Id] ASC);
GO
-- --------------------------------------------------
-- Creating all FOREIGN KEY constraints
-- --------------------------------------------------
-- Creating foreign key on [Pk_Passport_Id] in table 'PassportDetails'
ALTER TABLE [dbo].[PassportDetails]
ADD CONSTRAINT [FK__PassportD__Pk_Pa__5BE2A6F2]
FOREIGN KEY ([Pk_Passport_Id])
REFERENCES [dbo].[Person]
([Pk_Person_Id])
ON DELETE NO ACTION ON UPDATE NO ACTION;
GO

why code first want to create index column?

I have two table like below:
[Table("MyFlashCard")]
public partial class MyFlashCard
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MyFlashCard()
{
MyFlashCardPics = new HashSet<MyFlashCardPic>();
}
public int Id { get; set; }
public int? FaslID { get; set; }
public virtual FasleManJdl FasleManJdl { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MyFlashCardPic> MyFlashCardPics { get; set; }
}
[Table("MyFlashCardPic")]
public partial class MyFlashCardPic
{
public int Id { get; set; }
[ForeignKey("MyFlashCard")]
public int MyFlashCardId { get; set; }
public virtual MyFlashCard MyFlashCard { get; set; }
}
and a ModelBuilder:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyFlashCard>()
.HasMany(e => e.MyFlashCardPics)
.WithRequired(e => e.MyFlashCard)
.HasForeignKey(e => e.MyFlashCardId)
.WillCascadeOnDelete();
}
and when I add migration it will create the below code:
CreateTable(
"dbo.MyFlashCardPic",
c => new
{
Id = c.Int(nullable: false, identity: true),
MyFlashCardId = c.Int(nullable: false),
MyFlashCard_Id = c.Int(),
MyFlashCard_Id1 = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.MyFlashCard", t => t.MyFlashCard_Id)
.ForeignKey("dbo.MyFlashCard", t => t.MyFlashCard_Id1)
.ForeignKey("dbo.MyFlashCard", t => t.MyFlashCardId, cascadeDelete: true)
.Index(t => t.MyFlashCardId)
.Index(t => t.MyFlashCard_Id)
.Index(t => t.MyFlashCard_Id1);
I only have MyFlashCardId column in MyFlashCardPic table but it want to create another column like: MyFlashCard_Id, MyFlashCard_Id1
I want to know why this happens and how prevent it?
If I delete these columns from above migration,after creating database(with update-database command) it will throws below error when I want to use MyFlashCardPic entity
Invalid column name 'MyFlashCard_Id1' , Invalid column name 'MyFlashCard_Id'
and if I don't delete these columns from migration I have problem in editing flashcard that have pics like another question I have recently
How to find out context objects that one entity is attached to?
another point is that without
[ForeignKey("MyFlashCard")]
attribute it will create 3 index column and without
modelBuilder.Entity<MyFlashCard>()
.HasMany(e => e.MyFlashCardPics)
.WithRequired(e => e.MyFlashCard)
.HasForeignKey(e => e.MyFlashCardId)
.WillCascadeOnDelete();
in OnModeling it will create 4 index column
I think your problem is that you have both:
[ForeignKey("MyFlashCard")]
public int MyFlashCardId { get; set; }
and
public virtual MyFlashCard MyFlashCard { get; set; }
When adding the public virtual property you are prompting EF to set a foreign key relationship, which by default it does so with ClassNameId syntax. Because you have already created the FK yourself with the same name, it nevertheless still thinks it has to do something, so it creates another with 1 as a suffix. To get around the problem, remove your own ForeignKey entry and let EF do its stuff!
EDIT
I created a new MVC solution. Did the initial database create, then added a new class containing:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace MVCef.Models
{
[Table("MyFlashCard")]
public class MyFlashCard
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MyFlashCard()
{
MyFlashCardPics = new HashSet<MyFlashCardPic>();
}
public int Id { get; set; }
public int? FaslID { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MyFlashCardPic> MyFlashCardPics { get; set; }
}
[Table("MyFlashCardPic")]
public class MyFlashCardPic
{
public int Id { get; set; }
public virtual MyFlashCard MyFlashCard { get; set; }
}
}
Made appropriate changes in IdentityModel, created new migration and everything worked, as advertised - including foreign key creation. I suggest you try the same with a new solution. I think somewhere along the line, you have confused the hell out of EF!
You should either remove the [ForeignKey("MyFlashCard")] attribute or stop configuring it through model builder. Doing both together is the cause of your troubles.
How does index creation affect you in all this?
I inserted exactly your model (I only added another missing class with Id and Description properties). I inserted also the "double configuration" of the same foreign key.
These are the statements that EF 6.1.3 runs
ExecuteNonQuery==========
CREATE TABLE [FasleManJdls] (
[Id] int not null identity(1,1)
, [Description] varchar(50) null
);
ALTER TABLE [FasleManJdls] ADD CONSTRAINT [PK_FasleManJdls_873b808d] PRIMARY KEY ([Id])
ExecuteNonQuery==========
CREATE TABLE [MyFlashCardPic] (
[Id] int not null identity(1,1)
, [MyFlashCardId] int not null
);
ALTER TABLE [MyFlashCardPic] ADD CONSTRAINT [PK_MyFlashCardPic_873b808d] PRIMARY KEY ([Id])
ExecuteNonQuery==========
CREATE TABLE [MyFlashCard] (
[Id] int not null identity(1,1)
, [FaslID] int null
, [FasleManJdl_Id] int null
);
ALTER TABLE [MyFlashCard] ADD CONSTRAINT [PK_MyFlashCard_873b808d] PRIMARY KEY ([Id])
ExecuteNonQuery==========
CREATE INDEX [IX_MyFlashCardId] ON [MyFlashCardPic] ([MyFlashCardId])
ExecuteNonQuery==========
CREATE INDEX [IX_FasleManJdl_Id] ON [MyFlashCard] ([FasleManJdl_Id])
ExecuteNonQuery==========
ALTER TABLE [MyFlashCardPic] ADD CONSTRAINT [FK_MyFlashCardPic_MyFlashCard_MyFlashCardId] FOREIGN KEY ([MyFlashCardId]) REFERENCES [MyFlashCard] ([Id])
ExecuteNonQuery==========
ALTER TABLE [MyFlashCard] ADD CONSTRAINT [FK_MyFlashCard_FasleManJdls_FasleManJdl_Id] FOREIGN KEY ([FasleManJdl_Id]) REFERENCES [FasleManJdls] ([Id])
No strange columns, exactly as I'm expecting, one foreign key ([MyFlashCardPic].[MyFlashCardId] REFERENCES [MyFlashCard].[Id]).
I'm quite sure that the problem is somewhere else.
Wich EF are you using?
EDIT
Why your classes are marked as partial? Are you sure that you don't have other code around?

scaffold Many to Many Relation with nullable key

I got a table with 3 one to many relations (one many to many relation and a third one to many relation with extra data), but I want to scaffold some relationships from one side of the many to many relation. I don't want it to be linked to the other side of the many to many relationship, so I was thinking of making that nullable but I (with no surprises) can't do that as it is a primary key for it. But is there a workaround to get a null for one side of the Many- to many relation?
Here is the SQL source:
CREATE TABLE [dbo].[ConnectionPointRoutes] (
[ConnectionPointId] INT NOT NULL,
[RouteId] INT NOT NULL,
[SegmentId] INT NOT NULL,
[Position] INT NOT NULL,
CONSTRAINT [PK_dbo.ConnectionPointRoutes] PRIMARY KEY CLUSTERED ([ConnectionPointId] ASC, [RouteId] ASC, [SegmentId] ASC),
CONSTRAINT [FK_dbo.ConnectionPointRoutes_dbo.ConnectionPoints_ConnectionPointId] FOREIGN KEY ([ConnectionPointId]) REFERENCES [dbo].[ConnectionPoints] ([ConnectionPointId]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.ConnectionPointRoutes_dbo.Routes_RouteId] FOREIGN KEY ([RouteId]) REFERENCES [dbo].[Routes] ([RouteId]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.ConnectionPointRoutes_dbo.Segments_SegmentId] FOREIGN KEY ([SegmentId]) REFERENCES [dbo].[Segments] ([SegmentId]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_ConnectionPointId]
ON [dbo].[ConnectionPointRoutes]([ConnectionPointId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_RouteId]
ON [dbo].[ConnectionPointRoutes]([RouteId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_SegmentId]
ON [dbo].[ConnectionPointRoutes]([SegmentId] ASC);
And this is the Model, notice that I made it nullable but the database still puts it down as a Not nullable item
namespace InBuildingNavigator.Data.Models
{
public class ConnectionPointRoute
{
public int ConnectionPointId { get; set; }
public int? RouteId { get; set; }
public int? SegmentId { get; set; }
public int Position { get; set; }
public ConnectionPoint ConnectionPoint { get; set; }
public Route Route { get; set; }
public Segment Segment { get; set; }
}
}
Any thoughts on workarounds for this problem?
The problem got solved here: Optional One to many relationship
the connectionpointroute modelbuilder had to be like this :
modelBuilder.Entity<ConnectionPointRoute>()
.HasKey(c => new {c.ConnectionPointId, c.RouteId});

C# Nhibernate mapping to lookup table

I'm having a difficult time mapping an entity to another entity that is a lookup table.
create table Sources
(
SourceId identity(1,1) primary key not null,
Name [nvarchar](255) NULL,
)
create table Candidates
(
CandidateId int identity(1,1) primary key not null,
SourceId int references Sources(SourceId) NULL,
)
And Enitites:
public class Candidate : Entity
{
public virtual Source Source { get; set; }
}
public class Source : Entity
{
public virtual string Name { get; set; }
}
I'm getting a error:
An association from the table Candidates refers to an unmapped class:
Entities.Source
But I am unsure how to go about the mapping:
public class CandidateMap : IAutoMappingOverride<Candidate>
{
public void Override(AutoMapping<Candidate> mapping)
{
mapping.Map(x => x.Source);
}
}
The problem was I was inheriting from the Entity that was part of NHibernate namespace, instead of the SharpArch.NHibernate namespace. SharpArch does some mapping that the project I was working in was using.

EF6 + Code First + Fluent : Error Cannot insert the value NULL into column 'ID', table 'X' column does not allow nulls

There are tons of questions mentionning this error, I went through each one I found but they didn't correspond to my issue. Most of the time in the questions I found, the problem comes from the fact that the author is willing to manually give an ID to an entity but forgets to the switch the Autogenerated option to off.
My problem is exactly the opposit. I have several tables all containing a ID column which is automatically incremented by the database. The CRUDs operations are working fine on all of them but one.
I'm getting the terrible exception which is mentionned in the Title. I've spent 2 hours on this but I can't figure out why. Everything seems just fine.
Here is my model :
public class House
{
public int ID { get; set; }
public string Name {get;set;}
public DateTime CreationDate { get; set; }
public int CompanyID { get; set; }
public virtual BuildingCompany Assignation { get; set; }
public int? NoteID { get; set; }
public Note Note { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
public int? FileID { get; set; }
public virtual File File { get; set; }
}
Here is the mapping code
modelBuilder.Entity<House>().HasKey(r => r.ID);
modelBuilder.Entity<House>().Property(r => r.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<House>().HasRequired(r => r.Company).WithMany(a => a.Houses).HasForeignKey(r => r.CompanyID).WillCascadeOnDelete(false);
modelBuilder.Entity<House>().HasOptional(r => r.File).WithMany().HasForeignKey(r => r.FileID);
modelBuilder.Entity<House>().HasRequired(r => r.User).WithMany().HasForeignKey(r => r.UserID);
modelBuilder.Entity<House>().HasOptional(r => r.Note).WithRequired(n => n.House);
And here is the code I use to try to persist an entity (add a row) :
House house = new House
{
ID=0, // I also tried to remove this line
Name="Nice House",
CompanyID= // Some Integer,
CreationDate=DateTime.Now,
UserID= // Some Integer
};
context.Houses.Add(house)
context.Save();
This makes no sense to me, I've tried to debug and when my code hits the Save Method of my context, the Local cache contains the entity that I've created with the right parameters but then it throws me this error :
Error Cannot insert the value NULL into column 'ID', table 'Houses' column does not allow nulls.
Just as a reminder, I don't need/want to set the ID myself. I'm expecting that EF does it for me as it does with my other tables. That's why this problem particularly puzzles me.
Thanks for your help
Edit
Here is the Database Schema. The Database is auto-generated by EF. I only do Automatic Migrations after every model change. I don't touch at this level of detail.
CREATE TABLE [dbo].[Houses] (
[ID] INT NOT NULL,
[Name] NVARCHAR (MAX) NULL,
[CreationDate] DATETIME NOT NULL,
[CompanyID] INT NOT NULL,
[NoteID] INT NULL,
[UserID] INT NOT NULL,
[FileID] INT NULL,
CONSTRAINT [PK_dbo.Houses] PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_dbo.Houses_dbo.Files_FileID] FOREIGN KEY ([FileID]) REFERENCES [dbo].[Files] ([ID]),
CONSTRAINT [FK_dbo.Houses_dbo.Users_UserID] FOREIGN KEY ([UserID]) REFERENCES [dbo].[Users] ([ID]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.Houses_dbo.BuildingCompanies_CompaniesID] FOREIGN KEY ([CompanyID]) REFERENCES [dbo].[BuildingCompanies] ([ID])
);
GO
CREATE NONCLUSTERED INDEX [IX_CompanyID]
ON [dbo].[Houses]([CompanyID] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_UserID]
ON [dbo].[Houses]([UserID] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_FileID]
ON [dbo].[Houses]([FileID] ASC);
how is your database crearted ? are you sure the column is set to identity on the server ?
if not try to drop the database and re-run your app.

Categories