9

Ich habe ein simuliertes Tabellenvererbungskonstrukt in meinem SQL-Server basierend auf dem Artikel Implementing Table Inheritance in SQL Server implementiert."Eine abhängige Eigenschaft in einer ReferentialConstraint wird einer speichergenerierten Spalte zugeordnet." auf einer persistenten berechneten Spalte (zuerst EntityFramework DB)

Neben der Tatsache, einfache 1 zu 0 ... 1 Beziehungen zu verwenden, erstellen Sie eine weitere Einschränkung für eine Tabelle, die alle möglichen untergeordneten Typen der Basistabelle auflistet, wie im Artikel im Abschnitt "Modellierung von Entweder Constraints ".

Jede Ihrer untergeordneten Tabellen enthält ein TYPE-Feld mit einer ComputedColumnSpecification mit einer fortgesetzten Nummer, die die ID des Typs in der Typentabelle darstellt. Aufgrund der Tatsache, dass das TYPE-Feld Teil der Einschränkung ist, wird sichergestellt, dass nur ein Kind für den Basisdatensatz erstellt werden kann.

Zum besseren Verständnis habe ich eine Beispieldatenbank erstellt, die das Problem mit einer passenden ASP.NET-Lösung beschreibt. Um das Problem in Ihrer lokalen Umgebung zu replizieren, erstellen Sie eine Datenbank mit dem Namen „Spielwiese“, bevor Sie dieses Skript ausführen:

USE [PLAYGROUND] 
GO 
/****** Object: Table [dbo].[USER] ******/ 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE TABLE [dbo].[USER](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [TYPE__ID] [int] NOT NULL, 
    [Enabled] [bit] NOT NULL, 
    [Username] [nvarchar](32) NOT NULL, 
    [Password] [nchar](32) NOT NULL, 
    [Email] [nvarchar](255) NOT NULL, 
CONSTRAINT [PK_USER] 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 
/****** Object: Table [dbo].[NATURAL_USER] ******/ 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
SET ANSI_PADDING ON 
GO 
CREATE TABLE [dbo].[NATURAL_USER](
    [ID] [int] NOT NULL, 
    [TYPE] AS ((1)) PERSISTED NOT NULL, 
    [BirthDate] [date] NOT NULL, 
CONSTRAINT [PK_NATURAL_USER] 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 
/****** Object: Table [dbo].[JURIDICAL_USER] ******/ 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
SET ANSI_PADDING ON 
GO 
CREATE TABLE [dbo].[JURIDICAL_USER](
    [ID] [int] NOT NULL, 
    [TYPE] AS ((2)) PERSISTED NOT NULL, 
    [CompanyName] [nvarchar](256) NOT NULL, 
    [RegistrationNo] [nvarchar](max) NOT NULL, 
    [Description] [nvarchar](max) NOT NULL, 
CONSTRAINT [PK_LEGAL_USER] 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 
SET ANSI_PADDING OFF 
GO 
/****** Object: Table [dbo].[USER_T] ******/ 
SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE TABLE [dbo].[USER_T](
    [ID] [int] IDENTITY(1,1) NOT NULL, 
    [TYPE] [nvarchar](32) NOT NULL, 
CONSTRAINT [PK_USER_T] 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 
/****** Object: Index [IX_USER] ******/ 
ALTER TABLE [dbo].[USER] ADD CONSTRAINT [IX_USER] UNIQUE NONCLUSTERED 
(
    [Username] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 
/****** Object: Index [PK_USER_TYPE] ******/ 
CREATE UNIQUE NONCLUSTERED INDEX [PK_USER_TYPE] ON [dbo].[USER] 
(
    [ID] ASC, 
    [TYPE__ID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 
SET ANSI_PADDING ON 

GO 
/****** Object: Index [IX_USER_T] ******/ 
ALTER TABLE [dbo].[USER_T] ADD CONSTRAINT [IX_USER_T] UNIQUE NONCLUSTERED 
(
    [TYPE] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 
SET ANSI_PADDING ON 

GO 
/****** TYPE DATA ******/ 
SET IDENTITY_INSERT [dbo].[USER_T] ON 
GO 
INSERT [dbo].[USER_T] ([ID], [TYPE]) VALUES (2, N'JURIDICAL_USER') 
GO 
INSERT [dbo].[USER_T] ([ID], [TYPE]) VALUES (1, N'NATURAL_USER') 
GO 
SET IDENTITY_INSERT [dbo].[USER_T] OFF 
GO 
/****** Contraints ******/ 
ALTER TABLE [dbo].[JURIDICAL_USER] WITH CHECK ADD CONSTRAINT [FK_JURIDICAL_USER___USER] FOREIGN KEY([ID]) 
REFERENCES [dbo].[USER] ([ID]) 
GO 
ALTER TABLE [dbo].[JURIDICAL_USER] CHECK CONSTRAINT [FK_JURIDICAL_USER___USER] 
GO 
ALTER TABLE [dbo].[JURIDICAL_USER] WITH CHECK ADD CONSTRAINT [FK_JURIDICAL_USER___USER___TYPEVALIDATION] FOREIGN KEY([ID], [TYPE]) 
REFERENCES [dbo].[USER] ([ID], [TYPE__ID]) 
GO 
ALTER TABLE [dbo].[JURIDICAL_USER] CHECK CONSTRAINT [FK_JURIDICAL_USER___USER___TYPEVALIDATION] 
GO 
ALTER TABLE [dbo].[NATURAL_USER] WITH CHECK ADD CONSTRAINT [FK_NATURAL_USER___USER] FOREIGN KEY([ID]) 
REFERENCES [dbo].[USER] ([ID]) 
GO 
ALTER TABLE [dbo].[NATURAL_USER] CHECK CONSTRAINT [FK_NATURAL_USER___USER] 
GO 
ALTER TABLE [dbo].[NATURAL_USER] WITH CHECK ADD CONSTRAINT [FK_NATURAL_USER___USER___TYPEVALIDATION] FOREIGN KEY([TYPE]) 
REFERENCES [dbo].[USER_T] ([ID]) 
GO 
ALTER TABLE [dbo].[NATURAL_USER] CHECK CONSTRAINT [FK_NATURAL_USER___USER___TYPEVALIDATION] 
GO 
ALTER TABLE [dbo].[USER] WITH CHECK ADD CONSTRAINT [FK_USER___USER_T] FOREIGN KEY([TYPE__ID]) 
REFERENCES [dbo].[USER_T] ([ID]) 
GO 
ALTER TABLE [dbo].[USER] CHECK CONSTRAINT [FK_USER___USER_T] 
GO 
USE [master] 
GO 
ALTER DATABASE [PLAYGROUND] SET READ_WRITE 
GO 

Die Tabelle USER ist die Basistabelle und die Tische NATURAL_USER und JURIDICAL_USER sind seine Kinder. USER_T ist die Typentabelle von USER.

nun in meiner ASP.NET-Anwendung mit dem EntityFramework 6 ich versuche, einen neuen Benutzer die folgende Art und Weise zu erstellen:

using (PLAYGROUNDEntities model = new PLAYGROUNDEntities()) 
{ 
    USER user = new USER(); 
    user.Username = "admin"; 
    user.Password = "RANDOMHASH#123456"; 
    user.Email = "[email protected]"; 

    user.NATURAL_USER = new NATURAL_USER(); 
    user.NATURAL_USER.BirthDate = new DateTime(1980, 01, 01); 

    model.USER.Add(user); 
    model.SaveChanges(); 
} 

Und auf model.SaveChanges(); bekomme ich die Ausnahme:

einen abhängige Eigenschaft in einer ReferentialConstraint wird einer im Speicher generierten Spalte zugeordnet. Spalte: 'TYP'.

Probenlösung:.. https://dl.dropboxusercontent.com/u/55589036/zzzOther/Playground.zip (der Beispielcode ist in den Page_Load der Default.aspx.cs

Ich verstehe, die EntityFramework die Spalte Feld zu setzen versucht und versagt, weil es der Speicher erzeugen (beharrten) Diese selbst passiert, wenn ich user.NATURAL_USER.TYPE = 1; gesetzt.

ich versuchte OnModelCreating außer Kraft zu setzen meine eigene Regel zu befestigen und die beiden TYPE Spalten als Computed definieren, aber OnModelCreating wird nie genannt, weil ich EDMX tun -nach und ich möchte dabei bleiben.

Also dieses Entity-Modell wird basierend auf der Datenbank generiert und ich möchte es so behalten, und ich möchte keinen Code bearbeiten, wenn ich mein Modell jedes Mal neu aktualisiere.

Auch ich denke, dass die Tabelle Vererbungskonzept ist sehr gut auf der Datenbank-Ebene implementiert, da es keine Trigger verwendet. Ich möchte es auslöserfrei halten.

Wie kann ich dieses Problem lösen?

+0

Warum verwenden Sie nicht einfach EF mit seiner eigenen Vererbung (TPT oder TPH)? Haben Sie eine Projektbeschränkung für die Verwendung von DB First? – bubi

+0

@bubi TPT- oder TPH-Ansätze sind nur Vererbungssysteme für EF und erlauben dennoch, sie in der Datenbank zu umgehen, indem sie einfaches SQL verwenden. TPT/TPH erfordern, dass ich im Designer Verknüpfungen hinterlasse, nachdem ich die EDMX-Datei neu erstellt habe. Bitte korrigieren Sie mich, wenn ich hier falsch liege. – modiX

+0

TPT und TPH sind Ansätze, die sich auf Code First beziehen. Wenn Sie an einer vorhandenen Datenbank (DB First + EDMX) arbeiten, ist das kein guter Ansatz. Ihr Fall ist TPH (einzelne Tabelle + Diskriminatorfeld), aber wenn DB + EDMX ein Beschränkungsstandard ist, ist EF TPH kein guter Ansatz. – bubi

Antwort

2

ich einen schrecklichen Fehler gemacht habe, als das Konzept der Umsetzung, aber es funktionierte vor. Ich habe versehentlich die Einschränkung FK_NATURAL_USER___USER___TYPEVALIDATION vermasselt.

Es sollte wie die FK_JURIDICAL_USER___USER___TYPEVALIDATION Einschränkung gebaut werden.

Die EF kann die persistenten Spalten verarbeiten. Das Problem war, dass versucht wurde, auf die PK von [USER_T] zu schreiben, die überhaupt nicht von der Beschränkung sein sollte.

Es tut mir leid für alle Leute, die Zeit damit verschwendet haben.

2

Ich weiß nichts über EF, aber ich würde Ihre TYPE Spalten als normale Spalten erstellen, nicht berechnet, nicht beibehalten.

Dann würde ich ihren Standardwert auf den erforderlichen Wert festlegen und eine CHECK Einschränkung hinzufügen, um sicherzustellen, dass es nicht geändert werden kann.

Der Rest Ihres T-SQL-Skripts, in dem Ihre Fremdschlüssel eingerichtet sind, bleibt gleich.

Zum Beispiel für NATURAL_USER würde es so aussehen:

CREATE TABLE [dbo].[NATURAL_USER](
    [ID] [int] NOT NULL, 
    [TYPE] [int] NOT NULL, 
    [BirthDate] [date] NOT NULL, 
CONSTRAINT [PK_NATURAL_USER] PRIMARY KEY CLUSTERED 
(
    [ID] ASC 
)) 
GO 

ALTER TABLE [dbo].[NATURAL_USER] WITH CHECK 
ADD CONSTRAINT [CK_NATURAL_USER] CHECK (([TYPE]=(1))) 
GO 

ALTER TABLE [dbo].[NATURAL_USER] 
CHECK CONSTRAINT [CK_NATURAL_USER] 
GO 

ALTER TABLE [dbo].[NATURAL_USER] 
ADD CONSTRAINT [DF_NATURAL_USER_TYPE] DEFAULT ((1)) FOR [TYPE] 
GO