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

我的问题是某些数据库的方案与其他数据库略有不同。
在所有这些表上,我都有一个表X,该表具有一列Y,并且Y可以是一位或字节。

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

EF根据已将Y列列定义为字节的数据库生成了一个模型类。 因此,查询时显然会引发异常。



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

有没有一种方法可以通过数据库优先方法动态更改模型来解决此问题?还是在将返回值分配给模型之前将返回值强制转换为一个字节?防止异常?

最佳答案

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

模型文件

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

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

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

    metadata=res://*/...
    

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

    为简便起见,我将“映射和模型文件”称为“模型文件”。

    添加两组模型文件

    第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]
    

    在一个简单的C#控制台应用程序中,我根据该表创建了EDMX。

    第2步-添加部分文件

    在我的情况下,仅创建了一个Person类:
    public partial class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public bool IsActive { get; set; }
    }
    

    在EF中,必须将属性IsActive映射到bit数据库字段,因此不可能像已经发现的那样将其简单地映射到byte(或tinyint)字段。我们必须添加第二个属性来支持字节字段:
    partial class Person
    {
        public byte IsActiveByte { get; set; }
    }
    

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

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

    现在,第一组模型文件被嵌入到装配体中。我们希望将它们作为常规文件提供,以便进行复制和修改。这可以通过将设置“元数据工件处理”从默认设置(嵌入到输出程序集中)临时更改为来完成。复制到输出目录。现在构建项目,并在bin/Debug文件夹中找到三个文件。

    将“元数据工件处理”设置恢复为默认设置,将文件移动到项目的根目录,然后将其复制到第二组中。我最终得到了这些文件,其中的“BitModel”文件是原始文件:

    BitModel.csdl
    BitModel.msl
    BitModel.ssdl
    ByteModel.csdl
    ByteModel.msl
    ByteModel.ssdl
    

    为了使ByteModel文件支持Person.IsActiveByte属性,我进行了以下更改(原始行/编辑行):

  • csdl:
    <Property Name="IsActive" Type="Boolean" Nullable="false" />
    <Property Name="IsActiveByte" Type="Byte" Nullable="false" />
    
  • ssdl:
    <Property Name="IsActive" Type="bit" Nullable="false" />
    <Property Name="IsActive" Type="tinyint" Nullable="false" />
    
  • MSL:
    <ScalarProperty Name="IsActive" ColumnName="IsActive" />
    <ScalarProperty Name="IsActiveByte" ColumnName="IsActive" />
    

  • 现在可以删除BitModel文件。

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

    下一步是将ByteModel文件添加到项目中,并在其属性中将“生成操作”设置为“嵌入式资源”。重建项目。

    这些文件的嵌入与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" />
    

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

    <add name="DbBitContext"
        connectionString="metadata=res://*/BitOrByte.ByteModel.csdl
                                  |res://*/BitOrByte.ByteModel.ssdl
                                  |res://*/BitOrByte.ByteModel.msl;
        ... />
    

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

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

    局限性

    在LINQ到实体查询中,只能解决映射的属性。例如,类似...的查询
    context.People.Where(p => p.Id > 10).Select(p => p.Name).ToList()
    

    ...还可以。但是,当“BitModel”处于事件状态时,将出现如下查询:
    context.People.Where(p => p.IsActiveByte == 1).Select(p => p.Name).ToList()
    

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

    当然,您已经有了此限制。您可能想向类中添加未映射的属性,以将再见属性和位属性的值引导到将在应用程序代码中使用的一个属性。

    一种可能的解决方法是使用EntityFramework.DynamicFilters。这个小瑰宝使您可以在可以打开和关闭的上下文中定义全局过滤器。因此,可以定义两个全局过滤器...
    modelBuilder.Filter("IsActiveBit", (Person p) => p.IsActive, true);
    modelBuilder.Filter("IsActiveByte", (Person p) => p.IsActiveByte, 1);
    

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

    关于.net - 动态更改模型-数据库优先,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49026155/

    10-10 16:17
    查看更多