Parent Reference in Query DB when Creating database in LINQ - c#

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.

Related

Entity Framework Error: The field "ForeignKey" is required EF

I have an entity setup as follows
public class ExampleObject
{
[Key]
public int ID { get; set; }
public int RelationID { get; set; }
public string SomeValue { get; set; }
[ForeignKey("RelationID")]
public virtual RelationObj RelationObj { get; set; }
}
Later in my project I use this entity as follows
foreach (var rec in allRecs)
{
db.ExampleObject.Attach(rec);
rec.SomeValue = "TEST"
}
db.SaveChanges();
This throws a DbEntityValidationException "The field "RelationObj" is required"
I don't want to include the RelationObj when loading these records, that affects performance. Why is EF bothering to check the foreign relation object? How do I go about fixing this?
First create the db, change the database on top of the script to match your db
USE [Breaz]
GO
/****** Object: Table [dbo].[Relation] Script Date: 6/29/2016 4:35:32 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Relation](
[ID] [int] IDENTITY(1,1) NOT NULL,
[RelatedSomeValue] [varchar](10) NULL,
CONSTRAINT [PK_Relation] 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
SET ANSI_PADDING OFF
GO
Then create the next table, again change the db (Breaz):
USE [Breaz]
GO
/****** Object: Table [dbo].[Example] Script Date: 6/29/2016 4:34:15 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Example](
[ID] [int] IDENTITY(1,1) NOT NULL,
[RelationID] [int] NOT NULL,
[SomeValue] [varchar](10) NULL,
CONSTRAINT [PK_Example] 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
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[Example] WITH CHECK ADD CONSTRAINT [FK_Example_Relation] FOREIGN KEY([RelationID])
REFERENCES [dbo].[Relation] ([ID])
GO
ALTER TABLE [dbo].[Example] CHECK CONSTRAINT [FK_Example_Relation]
GO
Add a couple of rows of data into each table.
Add new item ADO.NET Entity Data Model, from your db and add both tables. Name the edmx ExampleModel. Get the (BreazEntities7) context (your context) from the Example.Context.cs file.
public class HomeController : Controller
{
public ActionResult YIndex()
{
using (BreazEntities7 db = new BreazEntities7())
{
var allRecs = db.Examples;
foreach (var rec in allRecs)
{
rec.SomeValue = "TEST";
}
db.SaveChanges();
}
return View();
}
Here is the generated Example:
namespace Testy2.Models
{
using System;
using System.Collections.Generic;
public partial class Example
{
public int ID { get; set; }
public int RelationID { get; set; }
public string SomeValue { get; set; }
public virtual Relation Relation { get; set; }
}
}
Here id the generated Relation:
namespace Testy2.Models
{
using System;
using System.Collections.Generic;
public partial class Relation
{
public Relation()
{
this.Examples = new HashSet<Example>();
}
public int ID { get; set; }
public string RelatedSomeValue { get; set; }
public virtual ICollection<Example> Examples { get; set; }
}
}

Mapping for nullable property results in 0 but not Null

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);)

ServiceStack OrmLite Join Issues

I'm having a problem with ServiceStack OrmLite for SQL Server in a Visual Studio 2013 C# project. My problem is that I'm trying to use the SqlExpression builder and it's not capturing my table schema and the generated SQL code is not correct. When I run the code, I get a System.Data.SqlClient.SqlException that says "Invalid object name 'ReportPages'."
I'm using the latest NuGet version of ServiceStack.OrmLite, which is version 4.0.24.
Let me start with the table setup. Note that I removed the foreign keys for convenience:
-- Create the report pages table.
CREATE TABLE [MicroSite].[ReportPages](
[ReportPageID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](200) NULL,
[Template] [nvarchar](50) NULL,
[AccessLevel] [int] NOT NULL,
[AssignedEmployeeId] [int] NOT NULL,
[Disabled] [bit] NOT NULL,
[Deleted] [bit] NOT NULL,
[ReportSectionID] [int] NOT NULL,
[Index] [int] NOT NULL,
[Cover] [bit] NOT NULL,
CONSTRAINT [PK_dbo.ReportSections] PRIMARY KEY CLUSTERED
(
[ReportPageID] 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
-- Create the report sections table.
CREATE TABLE [MicroSite].[ReportSections](
[ReportSectionID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NULL,
[ReportID] [int] NOT NULL,
CONSTRAINT [PK_dbo.ReportSectionGroups] PRIMARY KEY CLUSTERED
(
[ReportSectionID] 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
-- Create the report editables table.
CREATE TABLE [MicroSite].[Editables](
[EditableID] [int] IDENTITY(1,1) NOT NULL,
[Index] [int] NOT NULL,
[Content] [nvarchar](max) NULL,
[Styles] [nvarchar](100) NULL,
[Type] [int] NOT NULL,
[ReportPageID] [int] NOT NULL,
CONSTRAINT [PK_dbo.Editables] PRIMARY KEY CLUSTERED
(
[EditableID] 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
So my tables basically look like this:
Here are my POCOs:
[Alias("ReportPages")]
[Schema("MicroSite")]
public partial class MicrositeReportPage : IHasId<int>
{
[Required]
public int AccessLevel { get; set; }
[Required]
public int AssignedEmployeeId { get; set; }
[Required]
public bool Cover { get; set; }
[Required]
public bool Deleted { get; set; }
[Required]
public bool Disabled { get; set; }
[Alias("ReportPageID")]
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
[Required]
public int Index { get; set; }
public string Name { get; set; }
[Alias("ReportSectionID")]
[Required]
public int ReportSectionId { get; set; }
public string Template { get; set; }
}
[Alias("ReportSections")]
[Schema("MicroSite")]
public class MicrositeReportSection : IHasId<int>
{
[Alias("ReportSectionID")]
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
public string Name { get; set; }
[Alias("ReportID")]
[Required]
public int ReportId { get; set; }
}
[Alias("Editables")]
[Schema("MicroSite")]
public partial class MicrositeEditable : IHasId<int>
{
public string Content { get; set; }
[Alias("EditableID")]
[AutoIncrement]
[PrimaryKey]
public int Id { get; set; }
[Required]
public int Index { get; set; }
[Alias("ReportPageID")]
[Required]
public int ReportPageId { get; set; }
public string Styles { get; set; }
[Alias("Type")]
[Required]
public int TypeId { get; set; }
}
The actual SQL statement I want to generate is this:
SELECT e.EditableID
, e.[Index]
, e.Content
, e.Styles
, e.Type
, e.ReportPageID
FROM
MicroSite.ReportSections AS rs
LEFT JOIN
MicroSite.ReportPages AS rp ON rp.ReportSectionID = rs.ReportSectionID AND rp.[Index] = 24
LEFT JOIN
MicroSite.Editables AS e ON e.ReportPageID = rp.ReportPageID
WHERE
rs.ReportID = 15
Here is my C# code:
var query = db.From<MicrositeReportSection>()
.LeftJoin<MicrositeReportSection, MicrositeReportPage>((section, page) => section.Id == page.ReportSectionId)
.LeftJoin<MicrositeReportPage, MicrositeEditable>((page, editable) => page.Id == editable.ReportPageId && page.Index == 24)
.Where<MicrositeReportSection>(section => section.ReportId == 15);
var sql = query.ToSelectStatement();
var result = db.Select<MicrositeEditable>(query)
The generated SQL statement from the sql variable looks like this (I formatted it for readability):
SELECT
"ReportSectionID",
"Name",
"ReportID"
FROM
"MicroSite"."ReportSections"
LEFT JOIN
"ReportPages" ON ("ReportSections"."ReportSectionID" = "ReportPages"."ReportSectionID")
LEFT JOIN "Editables"
ON (("ReportPages"."ReportPageID" = "Editables"."ReportPageID") AND ("ReportPages"."Index" = 24))
WHERE
("ReportSections"."ReportID" = 15)
First, the left joins are missing the schema name, which makes the SQL statement incorrect. Second, what's going on in the select statement? Those aren't the columns from the MicrositeEditable table. Am I doing this correctly or is this an actual bug with OrmLite?
I submitted a ticket with the development team and this is now fixed in version 4.0.25.

Cannot insert the value NULL into column - NHibernate entity with 2 parents

{"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

NHibernate Error - "could not initialize a collection"

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();

Categories

Resources