【问题标题】:Dynamically set a table name in EF在 EF 中动态设置表名
【发布时间】:2014-01-23 15:01:31
【问题描述】:

我看过这个:

How can you dynamically select a table with entity framework 4.x?

但是,在我的情况下,我不能使用基类。我希望为审计创建一个用户组和周敏感表,所以我不知道在运行时创建它们之前会调用什么。

因此,如果我在一年中的第一周登录,那将是:

Group1_01_2014

这可能吗?我试图在创建时简单地更改数据库名称,但我得到了关于数据库迁移的标准异常。

谢谢。

编辑:在有人说拆分数据库很愚蠢之前,请知道我正在审核很多。这个想法是能够审计大多数交易,只要需要就可以存储它们,而不会严重影响性能。

Edit2:也就是说,如果有人有更好的解决方案,我会全力以赴。

解决方案

最终使用了存储过程:。

CREATE FUNCTION GetAuditTableName
(
  @db_code          CHAR (4)
)   
RETURNS CHAR (12)
BEGIN
    DECLARE @wkNo CHAR (2)
    DECLARE @year CHAR (4)

    SELECT @wkNo = REPLACE(STR(DATEPART(WEEK, GETDATE()), 2), SPACE(1), '0')
    SELECT @year = STR(DATEPART(YEAR, GETDATE()), 4)

    RETURN @db_code + '_' + @year + '_' + @wkNo
END

GO

CREATE PROCEDURE [dbo].[Audit_Insert]  
  @audi_id_first    NVARCHAR (20),
  @audi_id_second   NVARCHAR (20),
  @audi_by          NVARCHAR (100),
  @audi_on          DATETIME,
  @audi_details     XML,
  @tabn_code        CHAR (4),
  @audt_id          INT,
  @audi_db_code     CHAR (4)
AS  
BEGIN 
    DECLARE @tableName CHAR (12)
    SET @tableName = [dbo].GetAuditTableName(@audi_db_code)

    IF NOT EXISTS (SELECT * FROM sysobjects WHERE name=@tableName and xtype='U')
        BEGIN
            DECLARE @createSql NVARCHAR(MAX);
            SELECT @createSql = 'CREATE TABLE [dbo].' + QUOTENAME(@tableName) + ' (
                [audi_id]        INT            IDENTITY (1, 1) NOT NULL,
                [audi_id_first]  NVARCHAR (20) NULL,
                [audi_id_second] NVARCHAR (20) NULL,
                [audi_by]        NVARCHAR (100) NOT NULL,
                [audi_on]        DATETIME       NOT NULL,
                [audi_details]   XML            NULL,
                [tabn_code]      CHAR (4)       NULL,
                [audt_id]        INT            NOT NULL,
                CONSTRAINT [PK_dbo.' + @tableName + '] PRIMARY KEY CLUSTERED ([audi_id] ASC),
                CONSTRAINT [FK_dbo.' + @tableName + '_dbo.tabn_table_name_tabn_code] FOREIGN KEY ([tabn_code]) REFERENCES [dbo].[tabn_table_name] ([tabn_code]),
                CONSTRAINT [FK_dbo.' + @tableName + '_dbo.audt_audit_type_audt_id] FOREIGN KEY ([audt_id]) REFERENCES [dbo].[audt_audit_type] ([audt_id])
            )'
            EXEC sp_executesql @createSql
        END

    DECLARE @insertSql NVARCHAR(MAX);
    SELECT @insertSql = N'INSERT [dbo].' + QUOTENAME(@tableName) + ' ([audi_id_first], [audi_id_second], [audi_by], [audi_on], [audi_details], [tabn_code], [audt_id])
                         VALUES (@audi_id_first, @audi_id_second, @audi_by, @audi_on, @audi_details, @tabn_code, @audt_id)'
    EXEC sp_executesql @insertSql, N'@audi_id_first NVARCHAR (20), @audi_id_second NVARCHAR (20), @audi_by NVARCHAR (100), @audi_on DATETIME, @audi_details XML, @tabn_code CHAR (4), @audt_id INT', @audi_id_first, @audi_id_second, @audi_by, @audi_on, @audi_details, @tabn_code, @audt_id

    DECLARE @idSql NVARCHAR(MAX);
    SELECT @idSql = 'DECLARE @audi_id INT
        SELECT @audi_id = [audi_id]
        FROM [dbo].' + QUOTENAME(@tableName) + '
        WHERE @@ROWCOUNT > 0 AND [audi_id] = scope_identity()

        SELECT t0.[audi_id]
        FROM [dbo].' + QUOTENAME(@tableName) + ' AS t0
        WHERE @@ROWCOUNT > 0 AND t0.[audi_id] = @audi_id'
    EXEC sp_executesql @idSql
END

GO

然后从 C# 调用它:

    public void AddRecord(Audit record)
    {
        var Id1 = new SqlParameter("@audi_id_first", SqlDbType.NVarChar);
        Id1.Value = (object)record.Id1 ?? System.DBNull.Value;

        var Id2 = new SqlParameter("@audi_id_second", SqlDbType.NVarChar);
        Id2.Value = (object)record.Id2 ?? System.DBNull.Value;

        var UserName = new SqlParameter("@audi_by", SqlDbType.NVarChar);
        UserName.Value = record.UserName;

        var Stamp = new SqlParameter("@audi_on", SqlDbType.DateTime);
        Stamp.Value = record.Stamp;

        var Details = new SqlParameter("@audi_details", SqlDbType.Xml);
        Details.Value = (object)record.Details ?? System.DBNull.Value;

        var TableCode = new SqlParameter("@tabn_code", SqlDbType.Char);
        TableCode.Value = record.TableCode;

        var TypeId = new SqlParameter("@audt_id", SqlDbType.Int);
        TypeId.Value = record.TypeId;

        var DatabaseCode = new SqlParameter("@audi_db_code", SqlDbType.Char);
        DatabaseCode.Value = CallingPrefix;

        this.Database.ExecuteSqlCommand("EXEC Audit_Insert @audi_id_first, @audi_id_second, @audi_by, @audi_on, @audi_details, @tabn_code, @audt_id, @audi_db_code",
            Id1, Id2, UserName, Stamp, Details, TableCode, TypeId, DatabaseCode);
    }

我的下一个难题是让它处理 IQueryable OData 请求。

解决方案 2

CREATE PROCEDURE [dbo].[CreateAuditTableIfNoneExists]  
  @tableName        CHAR (12)
AS  
BEGIN 
    IF NOT EXISTS (SELECT * FROM sysobjects WHERE name=@tableName and xtype='U')
        BEGIN
            DECLARE @createSql NVARCHAR(MAX);
            SELECT @createSql = 'CREATE TABLE [dbo].' + QUOTENAME(@tableName) + ' (
                [audi_id]        INT            IDENTITY (1, 1) NOT NULL,
                [audi_id_first]  NVARCHAR (20) NULL,
                [audi_id_second] NVARCHAR (20) NULL,
                [audi_by]        NVARCHAR (100) NOT NULL,
                [audi_on]        DATETIME       NOT NULL,
                [audi_details]   XML            NULL,
                [tabn_code]      CHAR (4)       NULL,
                [audt_id]        INT            NOT NULL,
                CONSTRAINT [PK_dbo.' + @tableName + '] PRIMARY KEY CLUSTERED ([audi_id] ASC),
                CONSTRAINT [FK_dbo.' + @tableName + '_dbo.tabn_table_name_tabn_code] FOREIGN KEY ([tabn_code]) REFERENCES [dbo].[tabn_table_name] ([tabn_code]),
                CONSTRAINT [FK_dbo.' + @tableName + '_dbo.audt_audit_type_audt_id] FOREIGN KEY ([audt_id]) REFERENCES [dbo].[audt_audit_type] ([audt_id])
            )'
            EXEC sp_executesql @createSql
        END
END

并在数据库初始化时确保表存在:

public class AuditInitialiser : IDatabaseInitializer<AuditContext>
{
    public void InitializeDatabase(AuditContext context)
    {
        if (context.Database.CreateIfNotExists())
        {
            Seed(context);
        }

        var tableName = new SqlParameter("@tableName", SqlDbType.Char);
        tableName.Value = context.AuditTableName;
        context.Database.ExecuteSqlCommand("EXEC CreateAuditTableIfNoneExists @tableName", tableName);
    }

但这仅对我有用,因为我使用的是 API,所以是无状态的,并且每次都构造 DbContext。这样可以确保表格始终存在。如果我在规定的系统上进行锻炼,那将无法正常工作。

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    我不是存储过程的忠实拥护者,但这可能是您想要使用 EF 6 (EF5??) 绑定到存储过程的能力的情况。然后您可以传递表格以用作参数。看看这是否有帮助:How to call Stored Procedure in Entity Framework 6 (Code-First)?

    (你也可以使用 EntitySQL 来解决这个问题)

    【讨论】:

    • 存储过程似乎映射到插入、更新和删除但不读取。我是否理解如果我这样做了,我将无法返回数据?
    • 假设我可以执行原始 SQL 查询来获取数据。
    • 我相信您可以使用存储过程进行选择(支持返回多个结果集的过程是 EF 5 的一项功能,因此必须支持选择)。不过,我自己还没有做过。
    • 首先努力实际添加存储过程。我是代码优先。在没有 sql server manager 的情况下如何向数据库添加过程?谢谢。
    • 射击。我通常不使用迁移(不反对他们,只是不适合我们的工作流程)。我通常会使用 sql server manager。但是,看看这个:stackoverflow.com/questions/5701608/…
    猜你喜欢
    • 2018-02-01
    • 2019-05-21
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    • 2019-12-23
    相关资源
    最近更新 更多