创建唯一索引或约束以检查重叠的DateTime列

时间:2018-02-12 17:05:17

标签: c# sql sql-server database entity-framework

我正在使用EF 6.2.0EF Code-First.NET 4.7Azure SQL Database

我有一个EF Code-First模型,我需要在其上强制执行约束,以便新行允许与其他ValidFromUtc和{{的范围重叠1}} ValidToUtc列。我希望约束在数据库中强制执行并由EF维护。

在我的EF模型中,我有以下属性和DateTime

DataAnnotations

如果[Required, Key] public Guid Id { get; set; } [Required, Index("UniqueIndexName", IsUnique = true, Order = 1)] public Guid OwnerAccountId { get; set; } [Required, Index("UniqueIndexName", IsUnique = true, Order = 2)] public Guid PartnerAccountId { get; set; } // I need a range constraint between ValidFromUtc and ValidToUtc to be enforced somehow. [Required, Index("UniqueIndexName", IsUnique = true, Order = 3)] public DateTime ValidFromUtc { get; set; } [Required, Index("UniqueIndexName", IsUnique = true, Order = 4)] public DateTime ValidToUtc { get; set; } // Etc. etc. OwnerAccountId的值无法插入或更新,则我需要的是强制执行给定PartnerAccountIdValidFromUtc的行的方法与任何现有行重叠。

是否可以使用(按优先顺序)实现上述约束:

  • DataAnnotations?
  • Fluent API?
  • 计算列?
  • 插入/更新触发器?
  • 存储过程?

如果是这样,怎么办呢?哪种方法更好,为什么?

我现在能想到的唯一解决方案是创建一个插入/更新触发器。

任何指针都非常感谢!谢谢!

2 个答案:

答案 0 :(得分:1)

不幸的是,我认为Trigger是唯一的选择:

CREATE TABLE [dbo].[UniqueDates](
    [OwnerAccountId] [int] NOT NULL,
    [ParentAccountId] [int] NOT NULL,
    [ValidFromUtc] [datetimeoffset](7) NOT NULL,
    [ValidToUtc] [datetimeoffset](7) NOT NULL,
 CONSTRAINT [PK_UniqueDates] PRIMARY KEY CLUSTERED 
(
    [OwnerAccountId] ASC,
    [ParentAccountId] 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
INSERT [dbo].[UniqueDates] ([OwnerAccountId], [ParentAccountId], [ValidFromUtc], [ValidToUtc]) VALUES (1, 1, CAST(N'2018-01-01T00:00:00.0000000+00:00' AS DateTimeOffset), CAST(N'2018-01-03T00:00:00.0000000+00:00' AS DateTimeOffset))
GO
INSERT [dbo].[UniqueDates] ([OwnerAccountId], [ParentAccountId], [ValidFromUtc], [ValidToUtc]) VALUES (1, 2, CAST(N'2018-01-04T00:00:00.0000000+00:00' AS DateTimeOffset), CAST(N'2018-01-05T00:00:00.0000000+00:00' AS DateTimeOffset))
GO
INSERT [dbo].[UniqueDates] ([OwnerAccountId], [ParentAccountId], [ValidFromUtc], [ValidToUtc]) VALUES (2, 1, CAST(N'2018-01-06T00:00:00.0000000+00:00' AS DateTimeOffset), CAST(N'2018-01-08T00:00:00.0000000+00:00' AS DateTimeOffset))
GO
create trigger [trg_i_Test] on [UniqueDates]
INSTEAD OF INSERT
AS
BEGIN
   if exists (SELECT null
  FROM [TEST].[dbo].[UniqueDates] [from]
  Inner join [TEST].[dbo].[UniqueDates] [to] on ([from].ValidFromUtc between [to].ValidFromUtc and [to].ValidtoUtc) 
      and ([from].OwnerAccountId<>[to].OwnerAccountID or [from].ParentAccountId<>[to].ParentAccountID)
      )
   BEGIN
      RAISERROR ('These dates break the rules.' ,16,1)
      ROLLBACK TRANSACTION
   END 
   ELSE    
      BEGIN
         INSERT INTO [UniqueDates]
         SELECT *
         FROM inserted
   END    
END
GO
INSERT [dbo].[UniqueDates] ([OwnerAccountId], [ParentAccountId], [ValidFromUtc], [ValidToUtc]) VALUES (2, 4, CAST(N'2018-01-07T00:00:00.0000000+00:00' AS DateTimeOffset), CAST(N'2018-01-09T00:00:00.0000000+00:00' AS DateTimeOffset))
GO

答案 1 :(得分:0)

如果您希望所有来自EF,那么您必须使用EF Migrations。 (注意:我根本不是移民的粉丝)。这允许EF完全控制db(创建和迁移等),这也意味着你需要在C#代码中执行许多特定于数据库的东西(必填字段,字段类型等)。然后在种子例程中你会写一些代码,如:

context.Database.ExecuteSQLCommand("CREATE CONSTRAINT /* etc etc */");

(如果这只是两个原始的,您可以创建自己的属性以放置在EF模型上,然后使用反射来动态创建自定义约束。)