Zaimplementowałem symulowaną konstrukcję dziedziczenia tabel w moim SQL-Server na podstawie artykułu Implementing Table Inheritance in SQL Server."Właściwość zależna w ReferentialConstraint jest odwzorowana na kolumnę generowaną przez sklep." na utrwalonej kolumnie obliczeniowej (najpierw EntityFramework DB)
Poza tym, że używają prostych relacji od 1 do 0 ... 1, tworzysz kolejne ograniczenie do tabeli typów, która wymienia wszystkie możliwe typy dzieci tabeli bazowej, jak wyjaśniono w artykule w akapicie "Modelowanie One-to- Albo więzy ".
Każda z tabel podrzędnych zawiera pole TYPE, które ma ComputedColumnSpecification z utrzymywaną liczbą reprezentującą identyfikator typu w tabeli typów. Ze względu na to, że pole TYPE jest częścią ograniczenia, upewni się, że do podstawowego zbioru danych można utworzyć tylko jedno dziecko.
Dla lepszego zrozumienia stworzyłem przykładową bazę danych, która służy do opisu problemu za pomocą pasującego rozwiązania ASP.NET. Replikować problem w swoim środowisku lokalnym, należy utworzyć bazę danych o nazwie „BOISKO” przed wykonaniem tego skryptu:
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
tabela USER
jest tabela bazowa i stoły NATURAL_USER
i JURIDICAL_USER
są jej dzieci. USER_T
to tabela typów USER
.
Teraz w mojej aplikacji ASP.NET z wykorzystaniem EntityFramework 6 Staram się utworzyć nowego użytkownika w następujący sposób:
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();
}
I na model.SaveChanges();
otrzymuję wyjątek:
na utrzymaniu Właściwość w ReferentialConstraint jest odwzorowywana na kolumnę generowaną przez sklep. Kolumna: "TYP".
Roztwór próbki:.. https://dl.dropboxusercontent.com/u/55589036/zzzOther/Playground.zip (przykładowy kod znajduje się w Page_Load
z Default.aspx.cs
Rozumiem The EntityFramework próbuje ustawić pole kolumny i nie, bo to sklep generowane (utrwalone) to nawet dzieje, kiedy ustawić user.NATURAL_USER.TYPE = 1;
.
próbowałem przesłonić OnModelCreating
dołączyć własne reguły i określają zarówno TYPE
kolumny jako Computed
, ale OnModelCreating
nigdy nie nazywa, bo robię EDMX - po i chcę się do tego trzymać.
Tak więc ten model encji jest generowany na podstawie bazy danych i chciałbym go zachować w ten sposób, a także nie chcę edytować żadnego kodu, kiedy za każdym razem aktualizuję mój model.
Ponadto, myślę, że koncepcja dziedziczenia tabeli jest bardzo dobrze zaimplementowana w warstwie bazy danych, ponieważ nie używa wyzwalaczy. Chcę go nie uruchamiać.
Jak mogę rozwiązać ten problem?
Dlaczego po prostu nie używasz EF z własnym dziedziczeniem (TPT lub TPH)? Czy masz ograniczenie projektu dotyczące korzystania z DB First? – bubi
@bubi Podejścia TPT lub TPH są tylko systemami dziedziczenia na EF i nadal pozwalają na ominięcie go w bazie danych przy użyciu zwykłego SQL. TPT/TPH wymagają od mnie budowania asocjacji w projektancie po ponownym utworzeniu pliku EDMX. Proszę mnie poprawić, jeśli się mylę. – modiX
TPT i TPH są podejściami [ściśle] związanymi z Code First. Jeśli pracujesz na istniejącej bazie danych (DB First + EDMX) nie jest dobrym podejściem. Twoja sprawa to TPH (pojedyncza tabela + pole dyskryminatora), ale jeśli DB + EDMX jest standardem ograniczającym, EF TPH nie jest dobrym podejściem. – bubi