I have many-to-one mappings working fine, but a one-to-many relationship between locations and location_times keeps giving me an error.
I keep getting this error:
on this line of code:
Mappings look like this:
Location:
public virtual IList<LocationTimes> LocationTimes { get; set; }
public virtual int locationID { get; set; }
public virtual IList<LocationTimes> LocationTimes { get; set; }
public Location()
{
LocationTimes = new List<LocationTimes>();
}
Location Map:
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Table("Locations");
Id(x => x.locationID).Column("ID");
HasMany(x => x.LocationTimes)
.Inverse()
.Cascade.All();
Location Table:
CREATE TABLE [dbo].[Locations](
[ID] [int] IDENTITY(1,1) NOT NULL
...
CONSTRAINT [PK_Locations_1] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
LocationTimes:
public class LocationTimes
{
public virtual int ID { get; set; }
public virtual Location Location { get; set; }
}
LocationTimesMap:
public class LocationTimesMap : ClassMap<LocationTimes>
{
public LocationTimesMap()
{
Table("Location_Times");
Id(x => x.ID);
References(x => x.Location).Column("LID");
}
}
Location_times table:
CREATE TABLE [dbo].[Location_Times](
[ID] [int] IDENTITY(1,1) NOT NULL,
[LID] [int] NULL,
[EHStart] [int] NULL,
[EHEnd] [int] NULL,
[EHSell] [money] NULL,
CONSTRAINT [PK_Location_Times_1] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
The full error message:
"{"could not initialize a collection: [WhygoDomain.Location.LocationTimes#4]
[SQL: SELECT locationti0_.Location_id as Location4_1_, locationti0_.ID as ID1_, locationti0_.ID as ID1_0_, locationti0_.LID as LID1_0_, locationti0_.EHStart as EHStart1_0_
FROM Location_Times locationti0_
WHERE locationti0_.Location_id=?]"}"
I can see from the sql in the error message that it is indeed looking for locationti0_.Location_id, which I know doesn't exist. I don't know why it's looking for that though.
This is usually a problem of ID name mis-matches on your tables. Check to ensure that Location has an ID column on the table and that it follows your convention or is mapped correctly. You don't share Location's map, full object graph, or any of the tables so its hard to tell what the IDs are named and if they are matching up correctly.
Edit:
Per RichardD's answer in the comments, modify the LocationMap to be as follows:
public class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Table("Locations");
Id(x => x.locationID).Column("ID");
HasMany(x => x.LocationTimes).KeyColumn("LID").Inverse().Cascade.All();
Related
I have the following Classes
public class Children {
public Children (){} // constructor
public int Id {get;set;},
public string ChildName {get; set;}
}
public class Parents {
public Parents(){} // constructor
public int Id {get;set;},
public string ParentName {get;set;}
public Children Child {get; set;}
}
I want 1 child per parent, In the configuration I have
public void Configure(EntityTypeBuilder<Parents> configuration)
{
configuration.ToTable("Parents");
configuration.Property(p => p.Id).HasField("_id");
configuration.HasOne(a => a.ChildClass).WithMany().OnDelete(DeleteBehavior.Cascade);
}
public void Configure(EntityTypeBuilder<Children> configuration)
{
configuration.ToTable("Children");
configuration.Property(p => p.Id).HasField("_id");
}
When I use the entity framework 5 builder I get 2 tables back from the database
CREATE TABLE [dbo].[Parents](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ChildId] [int] NOT NULL,
[ParentName] [nvarchar](max) NULL,
CONSTRAINT [PK_Parents] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
WITH
(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
ALTER TABLE [dbo].[Parents] WITH CHECK ADD CONSTRAINT
[FK_Parents_Children_Child] FOREIGN KEY([ChildId])
REFERENCES [dbo].[Children] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Parents] CHECK CONSTRAINT [FK_Parents_Parents_Children_ChildId]
GO
CREATE TABLE [dbo].[Children](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ChildName] [nvarchar](max) NULL,
[ParentId] [int] NULL,
CONSTRAINT [PK_Children] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[Children] WITH CHECK ADD CONSTRAINT [FK_Children_Parent_ParentId] FOREIGN
KEY([ParentId])
REFERENCES [dbo].[Parents] ([Id])
GO
ALTER TABLE [dbo].[Children] CHECK CONSTRAINT [FK_Children_Parent_ParentId]
GO
Basically I get stuck in a loop as I cannot insert a Parents record, and I cannot insert a son as they need both sets of Id's, I dont know why the Children table needs a reference to the Parents table, if the parents table already references the Children table.
Based on your fluent mapping, it looks like you've set up a one-to-many relationship, where each child can have many parents. But you actually want a one-to-one relationship.
public class Children {
public Children (){}
public int Id { get;set; }
public string ChildName { get; set; }
public int ParentId { get; set; }
public Parents Parent { get; set; }
}
public class Parents {
public Parents(){}
public int Id { get; set; }
public string ParentName { get; set; }
public Children Child { get; set; }
}
public void Configure(EntityTypeBuilder<Parents> configuration)
{
configuration.ToTable("Parents");
configuration.Property(p => p.Id).HasField("_id");
configuration.HasOne(p => p.Child).WithOne(c => c.Parent).HasForeignKey<Children>(c => c.ParentId).OnDelete(DeleteBehavior.Cascade);
}
However, the generated SQL doesn't seem to match your model nor your fluent configuration.
I am getting the "Invalid column name" error and I strongly believe its because my mapping is wrong.
I am using C# and database first.
My tables are as follow:
icotb001_unidades_c ---- gedtbd042_posto_atendimento_c ------ gedtbd039_posto_atendimento_m
(id)nu_unidade ------------ (id)nu_unidade ------------------------------ (id)co_posto
no_unidade----------------- (fk)co_posto---------------------------------- no_posto
Trying to make it clear:
icotb001_unidades_c-- 0..1:1 -- gedtbd042_posto_atendimento_c -- M:1 -- gedtbd039_posto_atendimento_m
gedtbd042_posto_atendimento_c is inheriting from icotb001_unidades_c. That's why it's not a typcal on-to-many relatioship.
Using database first, the table gedtbd042_posto_atendimento_c was not created and the other tables has the following code:
public partial class icotb001_unidades_c
{
public icotb001_unidades_c ()
{
}
[Key]
public string nu_unidade { get; set; }
public string no_unidade{ get; set; }
public virtual gedtbd039_posto_atendimento_m gedtbd039_posto_atendimento_m { get; set; }
}
public partial class gedtbd039_posto_atendimento_m
{
public gedtbd039_posto_atendimento_m()
{
icotb001_unidades_c = new HashSet<icotb001_unidades_c>();
}
[Key]
[StringLength(9)]
public string co_posto { get; set; }
[Required]
public string no_posto { get; set; }
public virtual ICollection<icotb001_unidades_c> icotb001_unidades_c { get; set; }
}
It wasn't created any fluent API between then either. I searched MSDN and this answered question, but couldn't get it working.
Any ideas?
EDIT
I changed the above table names to make it clear with the table statements below.
CREATE TABLE [dbo].[gedtbd039_posto_atendimento_m](
[co_posto] [char](9) NOT NULL,
[no_posto] [varchar](100) NOT NULL,
CONSTRAINT [PK_gedtbd039_posto_atendimento] PRIMARY KEY CLUSTERED
(
[co_posto] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 98) ON [PRIMARY]
) ON [PRIMARY]
---------
CREATE TABLE [dbo].[icotb001_unidades_c](
[nu_unidade] [char](4) NOT NULL,
[no_unidade] [varchar](70) NOT NULL,
CONSTRAINT [PK_icotb001_unidades_c] PRIMARY KEY CLUSTERED
(
[nu_unidade] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 98) ON [PRIMARY]
) ON [PRIMARY]
---------
CREATE TABLE [dbo].[gedtbd042_posto_atendimento_c](
[co_posto] [char](9) NOT NULL,
[nu_unidade] [char](4) NOT NULL,
CONSTRAINT [PK_gedtbd042_vinculacao_posto_atendimento] PRIMARY KEY CLUSTERED
(
[nu_unidade] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 98) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[gedtbd042_posto_atendimento_c] WITH CHECK ADD CONSTRAINT [FK_gedtbd042_vinculacao_posto_atendimento_gedtbd039_posto_atendimento] FOREIGN KEY([co_posto])
REFERENCES [dbo].[gedtbd039_posto_atendimento_m] ([co_posto])
GO
ALTER TABLE [dbo].[gedtbd042_posto_atendimento_c] CHECK CONSTRAINT [FK_gedtbd042_vinculacao_posto_atendimento_gedtbd039_posto_atendimento]
GO
ALTER TABLE [dbo].[gedtbd042_posto_atendimento_c] WITH CHECK ADD CONSTRAINT [FK_gedtbd042_vinculacao_posto_atendimento_icotb001_unidades_c] FOREIGN KEY([nu_unidade])
REFERENCES [dbo].[icotb001_unidades_c] ([nu_unidade])
GO
ALTER TABLE [dbo].[gedtbd042_posto_atendimento_c] CHECK CONSTRAINT [FK_gedtbd042_vinculacao_posto_atendimento_icotb001_unidades_c]
GO
Ok this is driving me insane.
I have tried to build a few tables using Code First but I cannot get it to do what I have asked.
So I have used SQL Management studio to create my tables which I will post now:
CREATE TABLE [dbo].[Products](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nchar](10) NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
and for the product detail I have
CREATE TABLE [dbo].[ProductDetail](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Title] [nchar](10) NULL,
[Summary] [nchar](10) NULL,
[ProductId] [int] NOT NULL,
CONSTRAINT [PK_ProductDetail] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[ProductDetail] WITH CHECK ADD CONSTRAINT [FK_ProductDetail_Products] FOREIGN KEY([ProductId])
REFERENCES [dbo].[Products] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[ProductDetail] CHECK CONSTRAINT [FK_ProductDetail_Products]
GO
As you can see, if I delete a Product then the ProductDetails will also be deleted.
But if I delete my ProductDetails it does not cascade.
For some reason I am having great difficulty in replicating that in Code First EF.
Can someone help me please?
I've always generate my code first domain classes and mappings using Entity Framework Power Tools and it works great. I've used this tool to generate code first classes on the base of your two tables, posted above. Here they are:
public partial class Product
{
public Product()
{
this.ProductDetails = new List<ProductDetail>();
}
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ProductDetail> ProductDetails { get; set; }
}
public class ProductMap : EntityTypeConfiguration<Product>
{
public ProductMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.Name)
.IsFixedLength()
.HasMaxLength(10);
// Table & Column Mappings
this.ToTable("Products");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Name).HasColumnName("Name");
}
}
public partial class ProductDetail
{
public int Id { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
public class ProductDetailMap : EntityTypeConfiguration<ProductDetail>
{
public ProductDetailMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.Title)
.IsFixedLength()
.HasMaxLength(10);
this.Property(t => t.Summary)
.IsFixedLength()
.HasMaxLength(10);
// Table & Column Mappings
this.ToTable("ProductDetail");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Title).HasColumnName("Title");
this.Property(t => t.Summary).HasColumnName("Summary");
this.Property(t => t.ProductId).HasColumnName("ProductId");
// Relationships
this.HasRequired(t => t.Product)
.WithMany(t => t.ProductDetails)
.HasForeignKey(d => d.ProductId);
}
}
I have the following mapping defined via FluentMapping
public class RuleMap : ClassMap<Rule>
{
public RuleMap()
{
Table("NEW");
Id(x => x.Id, "Id").Not.Nullable();
Map(x => x.SenderId, "SenderId").Nullable();
}
}
for the following class
public class Rule
{
public virtual int Id { get; set; }
public virtual int? SenderId { get; set; }
}
My Table in Database is defined as
USE [Test]
GO
/****** Object: Table [dbo].[NEW] Script Date: 23.04.2015 22:14:53 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[NEW](
[Id] [int] NOT NULL,
[SenderId] [int] NULL,
CONSTRAINT [PK_NEW] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
and has a single Entry with
Id | SenderId
1 | `NULL`
Now when I load all Rules via my SessionFactory
var rules = sessionFactory.QueryOvery<Rule>.List();
the list contains 1 Item (so far so good), but the property SenderId has the value 0 and not NULL what I have expected.
So what am I doing wrong here?
Using NHibernate 3.1 and FluentNHibernate 1.2.
Change your mapping to,
public class NullableMap : ClassMap<Nullable>
{
public NullableMap()
{
Table("nullable");
LazyLoad();
Id(x => x.Id).GeneratedBy.Identity().Column("id");
Map(x => x.SenderId, "SenderId").Nullable().Default(null);
}
}
where default value is set to null (.Default(null);)
{"Cannot insert the value NULL into column 'RootID', table 'Legacy.dbo.Middle'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."}
I have a Root class, Middle class, and 'bottom' class. The root class as "Middle" as children, then Middle has "Bottom" as children. However, in this legacy database with poor design there is also a reference to "Root" from the "Bottom".
root table
CREATE TABLE [dbo].[Root](
[RootID] [int] IDENTITY(1,1) NOT NULL,
[RootName] [varchar](max) NOT NULL,
CONSTRAINT [PK_Root] PRIMARY KEY CLUSTERED
(
[RootID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
middle table
CREATE TABLE [dbo].[Middle](
[MiddleID] [int] IDENTITY(1,1) NOT NULL,
[MiddleName] [varchar](max) NOT NULL,
[RootID] [int] NOT NULL,
CONSTRAINT [PK_Middle] PRIMARY KEY CLUSTERED
(
[MiddleID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
bottom table
CREATE TABLE [dbo].[Bottom](
[BottomID] [int] IDENTITY(1,1) NOT NULL,
[BottomName] [varchar](max) NOT NULL,
[MiddleID] [int] NOT NULL,
[RootID] [int] NOT NULL,
CONSTRAINT [PK_Bottom] PRIMARY KEY CLUSTERED
(
[BottomID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
the foreign keys
ALTER TABLE [dbo].[Middle] WITH CHECK ADD CONSTRAINT [FK_Middle_Root] FOREIGN KEY([RootID])
REFERENCES [dbo].[Root] ([RootID])
GO
ALTER TABLE [dbo].[Middle] CHECK CONSTRAINT [FK_Middle_Root]
ALTER TABLE [dbo].[Bottom] WITH CHECK ADD CONSTRAINT [FK_Bottom_Middle] FOREIGN KEY([MiddleID])
REFERENCES [dbo].[Middle] ([MiddleID])
GO
ALTER TABLE [dbo].[Bottom] CHECK CONSTRAINT [FK_Bottom_Middle]
GO
ALTER TABLE [dbo].[Bottom] WITH CHECK ADD CONSTRAINT [FK_Bottom_Root] FOREIGN KEY([RootID])
REFERENCES [dbo].[Root] ([RootID])
GO
ALTER TABLE [dbo].[Bottom] CHECK CONSTRAINT [FK_Bottom_Root]
c# entities
public class Root
{
public Root()
{
Middles = new SortedSet<Middle>();
}
public int RootID { get; set; }
public string RootName { get; set; }
public ISet<Middle> Middles { get; set; }
}
public class Middle
{
public Middle()
{
Bottoms = new SortedSet<Bottom>();
}
public int MiddleID { get; set; }
public Root Root
{
get;
set;
}
public string MiddleName { get; set; }
public ISet<Bottom> Bottoms { get; set; }
}
public class Bottom
{
public int BottomID { get; set; }
public Root Root { get; set; }
public Middle Middle { get; set; }
public string BottomName { get; set; }
}
I am using the following mappings via FluentNHibernate, but I've tried a ton of variations and just cannot get it to work in any way.
public class RootMap : IAutoMappingOverride<Root>
{
public void Override(AutoMapping<Root> mapping)
{
mapping.Not.LazyLoad();
mapping.Id(x => x.RootID);
mapping.HasMany(x => x.Middles).KeyColumn("RootID").Cascade.AllDeleteOrphan().Inverse();
}
}
public class MiddleMap : IAutoMappingOverride<Middle>
{
public void Override(AutoMapping<Middle> mapping)
{
mapping.Not.LazyLoad();
mapping.Id(x => x.MiddleID);
mapping.References(x => x.Root);
mapping.HasMany(x => x.Bottoms).KeyColumn("MiddleID").Cascade.AllDeleteOrphan();
}
}
public class BottomMap : IAutoMappingOverride<Bottom>
{
public void Override(AutoMapping<Bottom> mapping)
{
mapping.Not.LazyLoad();
mapping.Id(x => x.BottomID);
mapping.References(x => x.Root);
mapping.References(x => x.Middle);
}
}
You need to declare the many valued side of the relation as "inverse". See last paragraph of section 6.4: http://nhibernate.info/doc/nh/en/index.html#collections-onetomany