动态更改模型 - 数据库优先

Sta*_*avm 8 .net entity-framework entity-framework-6

我的数据库是使用数据优先方法的EF 6.0的MS-SQL.我正在与数百个数据库同步(表格方案在所有数据库上几乎相同),在需要时动态更改连接字符串.

我的问题是一些数据库的方案与其他数据库略有不同.在所有这些中我有一个表X,它有一列Y,Y可以是一个位,也可以是一个字节.

sql表显示

EF基于数据库生成了一个模型类,该数据库的列Y 定义为byte.所以在查询时,它显然会引发异常.

表'X'上的'Y'属性无法设置为System.Boolean值.您必须将值设置为System.Byte.

异常图像

有没有办法,在Database-First方法中动态更改模型来解决此问题?或者可能在返回值之前将其返回到一个字节,然后再将其分配给模型?防止例外?

Ger*_*old 5

有一种方法可以在数据库优先完成.简而言之:创建两组映射和模型文件,并在配置文件中选择一组.

模型文件

创建EDMX时,EF会创建三个文件:

  • 商店模型(*.ssdl).
  • 类(或概念)模型(*.csdl).
  • 这两个模型之间的映射(*.msl).

这些文件作为资源文件嵌入到已编译的程序集中,通常您不需要知道它们的存在.在运行时,EF将从程序集加载文件,由配置文件的连接字符串中的资源路径指示,通常看起来像......

metadata=res://*/...
Run Code Online (Sandbox Code Playgroud)

可以将另一组资源文件嵌入到程序集中并相应地修改连接字符串,但实现此目的需要几个步骤.

为简洁起见,我将"映射和模型文件"称为"模型文件".

添加两组模型文件

第1步 - 创建第一组

创建第一组文件只不过是创建EDMX.我使用了一个非常简单的数据库表:

CREATE TABLE [dbo].[Person](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
    [IsActive] [bit] NOT NULL,
    CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED ([Id] ASC))
ALTER TABLE [dbo].[Person] ADD  CONSTRAINT [DF_Person_IsActive]  DEFAULT ((1)) FOR [IsActive]
Run Code Online (Sandbox Code Playgroud)

在一个简单的C#控制台应用程序中,我在此表中创建了一个EDMX.

第2步 - 添加部分文件

在我的例子中,只Person创建了一个类:

public partial class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool IsActive { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在EF中,属性IsActive必须映射到bit数据库字段,因此不可能简单地将其映射到byte(或tinyint)字段,如您已经发现的那样.我们必须添加第二个属性来支持字节字段:

partial class Person
{
    public byte IsActiveByte { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

主要的挑战是如何根据数据类型将这两个属性中的任何一个映射到数据库中的一个字段.

第3步 - 复​​制并修改第二组

现在,第一组的模型文件嵌入在程序集中.我们希望将它们作为常规文件提供,以便复制和修改它们.这可以通过临时将"Metadata Artifact Processing"设置从其默认设置(嵌入输出程序集)更改为Copy to Output Directory来完成.现在构建项目并在bin/Debug文件夹中找到三个文件.

将"元数据工件处理"设置恢复为默认设置,将文件移动到项目的根目录并将其复制到第二个集合中.我最终得到了这些文件,其中"BitModel"文件是原件:

BitModel.csdl
BitModel.msl
BitModel.ssdl
ByteModel.csdl
ByteModel.msl
ByteModel.ssdl
Run Code Online (Sandbox Code Playgroud)

对于支持Person.IsActiveByte属性的ByteModel文件,我进行了这些更改(原始行/编辑行):

现在可以删除BitModel文件.

第4步 - 将第二组嵌入为资源

下一步是将ByteModel文件添加到项目中,并在其属性中将"Build Action"设置为"Embedded Resource".重建项目.

这些文件的嵌入方式与EF最初的方式略有不同.在反汇编程序中检查.exe文件显示<namespace>.<filename>,在我的情况下,它们的资源名称是:BitOrBye.ByteModel.csdl等等.

第5步 - 添加连接字符串

EF为项目添加了一个连接字符串,看起来像......

<add name="DbBitContext" 
    connectionString="metadata=res://*/BitModel.csdl
                              |res://*/BitModel.ssdl
                              |res://*/BitModel.msl;
    provider=System.Data.SqlClient;
    provider connection string=&quot;data source=.\sql2016;initial catalog=DbBit;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;"
    providerName="System.Data.EntityClient" />
Run Code Online (Sandbox Code Playgroud)

我复制了这个连接字符串并注释掉了原来的字符串.在复制的连接字符串中,我修改了资源路径:

<add name="DbBitContext" 
    connectionString="metadata=res://*/BitOrByte.ByteModel.csdl
                              |res://*/BitOrByte.ByteModel.ssdl
                              |res://*/BitOrByte.ByteModel.msl;
    ... />
Run Code Online (Sandbox Code Playgroud)

现在,程序集已准备好连接到Person.IsActive字段为a 的数据库tinyint.该属性Person.IsActive不再是映射属性,Person.IsActiveByte是.

输入先前的连接字符串,并将上下文映射到该bit字段,因此现在可以使用连接字符串来确定支持哪种类型的数据库,"BitModel"或"ByteModel".

限制

在LINQ-to-Entities查询中,只能映射映射的属性.例如,像...这样的查询

context.People.Where(p => p.Id > 10).Select(p => p.Name).ToList()
Run Code Online (Sandbox Code Playgroud)

......没关系 但是当"BitModel"处于活动状态时,查询就像......

context.People.Where(p => p.IsActiveByte == 1).Select(p => p.Name).ToList()
Run Code Online (Sandbox Code Playgroud)

...将抛出臭名昭着LINQ to Entities异常不支持指定的类型成员'IsActiveByte'.

当然你已经有了这个限制.您可能希望将未映射的属性添加到类中,这些属性将bye和bit属性的值传递给您将在应用程序代码中使用的一个属性.

一种可能的出路是使用EntityFramework.DynamicFilters .这个小宝石使您可以在可以打开和关闭的上下文中定义全局过滤器.因此,可以定义两个全局过滤器......

modelBuilder.Filter("IsActiveBit", (Person p) => p.IsActive, true);
modelBuilder.Filter("IsActiveByte", (Person p) => p.IsActiveByte, 1);
Run Code Online (Sandbox Code Playgroud)

...您将添加一个,具体取决于您连接的数据库类型,可以从连接字符串中推断出来.


Max*_*Max -1

您可以为实体创建一个分部类并添加一个可以处理任何情况的属性:

public partial class X
{
    public int TrueY
    {
        //add verification logic here
    }
}
Run Code Online (Sandbox Code Playgroud)