在EF中动态设置表名

时间:2014-01-23 15:01:32

标签: c# entity-framework

我见过这个:

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

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

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

Group1_01_2014

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

感谢。

编辑:在有人说拆分数据库之前很傻,知道我正在审核ALOT。我们的想法是能够审核大多数交易,只要需要存储它们,而不会严重影响性能。

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。这可确保表始终存在。如果我在一个规定的系统上工作,那就行不通了。

1 个答案:

答案 0 :(得分:2)

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

(您也可以使用EntitySQL来解决此问题)