使用 EF Core 创建视图

Create View using EF Core

按照此 blog post 中的指导,我添加了一个 ef 核心 (dotnet 6) 迁移,我将 t-sql 粘贴到其中以创建视图。

protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql(@"
            exec('create view [dbo].[vwSpotlightExtract] as
            with 
            exceptions as (
                select id from dbo.Application
                where CTaxNumber in (
                    select 
                    app.CTaxNumber
                    from dbo.Application app
                    group by app.CTaxNumber
                    having count(*) > 1
                )
                union
                -- duplicate bank account
                select id from dbo.Application where concat(BankSortCode, ':', BankAccountNumber) in (
                    select concat(app.BankSortCode, ':', app.BankAccountNumber)
                    from dbo.Application app
                    where app.BankAccountNumber is not null
                    group by concat(app.BankSortCode, ':', app.BankAccountNumber)
                    having count(*) > 1
                )
                union
                -- duplicate uprn
                select id from dbo.Application where uprn in (
                    select app.uprn from dbo.Application app
                    group by app.uprn having count(*)>1
                    )
                ),
            LastAppStatus as (
                select app.Id,  app.UTRN,
                max(rh.Id) LastRebateHistoryID
                from dbo.Application app
                inner join dbo.RebateHistory rh on app.ID = rh.ApplicationId    
                where rh.ApplicationId is not null --and rh.ApplicationId not in (select ID from exceptions)
                and app.RequestType = 0 -- BACS
                and ISNULL(app.PaymentStopped, 0) = 0 -- Payment NOT Stopped    
                --and app.id not in (select id from exceptions) -- to prevent sending stuff to spotlight 
                group by app.Id, app.UTRN
            )

            select 
            app.UTRN [Application Number],
            'Personal' [Personal or Business Bank Account? (required)], -- always Personal
            REPLACE(app.BankSortCode, '-', '') [Sort code (required)],
            app.BankAccountNumber [Account number (required)],
            NULL [Business name (required if business)],
            app.AccountPayerFirstName [First Name (required if personal)],
            app.AccountPayerSurname [Surname (required if personal)],
            CASE WHEN LEN(CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)) > 0 then CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx, '-', addr.SAO_End_No, addr.SAO_End_Sfx)
                WHEN LEN(CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)) = 0 then CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)
                WHEN LEN(CONCAT(addr.SAO_Start_No, addr.SAO_Start_Sfx)) = 0 and LEN (CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)) > 0 then CONCAT(addr.SAO_End_No, addr.SAO_End_Sfx)
            else NULL end as
            [Flat number (optional)], -- secondary addressable number or name
            case when LEN(addr.PAO_Desc) > 0 then addr.PAO_Desc
                when LEN(addr.SAO_Desc) < 0 then addr.SAO_Desc
                ELSE NULL
                end as [Building name (optional)],-- primary addressable name
            CASE WHEN LEN(CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)) > 0 then CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx, '-', addr.PAO_End_No, addr.PAO_End_Sfx)
                WHEN LEN(CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)) > 0 and LEN (CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)) = 0 then CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)
                WHEN LEN(CONCAT(addr.PAO_Start_No, addr.PAO_Start_Sfx)) = 0 and LEN (CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)) > 0 then CONCAT(addr.PAO_End_No, addr.PAO_End_Sfx)
                end  [Building number (optional)],
            addr.Street_Name [Street name (required)],
            addr.Postcode [Address postcode (required)],
            case when app.AccountType = 0 then 'Single'
                when app.AccountType = 1 then 'Joint'
                end as [Single or Joint account type (if personal)], -- new field coming
            convert(varchar, app.AccountPayerDOB, 103) [Date of birth (if personal)],
            NULL [Limited or non-limited (if business)],
            NULL [Company registration number (if business)]
            , ec.EventName
            , app.ID
            from dbo.Application app
            left outer join dbo.AcademyNonDDPayers nondd on app.CTaxNumber = nondd.CTaxNumber
            inner join dbo.CTaxAddress addr on RIGHT('000000000000'+ISNULL(app.uprn,''),12) = addr.uprn and nondd.CTaxPropertyRef = addr.CTaxPropertyRef
            inner join LastAppStatus las on app.ID = las.ID
            inner join dbo.RebateHistory rh on las.LastRebateHistoryID = rh.ID
            inner join dbo.EventCode ec on rh.EventCodeId = ec.ID
            where ec.ID = 11')
            ");
    }

这会导致发布管道出现故障。如果我从部署工件中获取 sql 脚本的内容并粘贴到 SSMS 中,则会看到以下错误:

我不确定这是为什么,因为创建视图语句似乎正确地包含了开始和结束语句:

CREATE VIEW不能在IF/BEGIN逻辑里面;它必须是批处理中唯一的语句。典型的解决方法是创建如下脚本:

IF OBJECT_ID(N'dbo.viewname', N'V') IS NULL
BEGIN
  EXEC sys.sp_executesql N'CREATE VIEW dbo.viewname AS SELECT 1;';
END
GO

ALTER VIEW dbo.viewname
AS
  ... real view code ...

在较新版本的 SQL 服务器中,您可以这样做:

CREATE OR ALTER VIEW dbo.viewname
AS
  ... real view code ...

但这仍然必须存在于自己的批次中。这意味着它不能在 IF/BEGIN 内,除非您对整个视图 使用动态 SQL 。例如:

IF (some condition)
BEGIN
  EXEC sys.sp_executesql N'CREATE VIEW dbo.viewname
       AS
         ... all that view code ...;';
END

您是否能够从 EF 核心生成任何这些表单,我只是不知道。有时我们希望 ORM 可以做所有事情,但通常它只能做一个非常小的子集。

无论如何,我不确定您要达到的逻辑。 (如果某行在某些 table 中不存在,请创建一个视图?那只会起作用一次。)