65.9K
CodeProject 正在变化。 阅读更多。
Home

在实时项目中管理 Entity Framework Code First

2014 年 7 月 29 日

CPOL

6分钟阅读

viewsIcon

19427

downloadIcon

120

如何在实时项目中管理 Entity Framework Code First

引言

Entity framework 是一个非常棒的框架。而 Code First 的方法也越来越受欢迎。但如果我们不恰当地使用 Entity Framework Code First,它可能会变成一场灾难。所以在这篇文章中,我将尝试说明我在使用 Entity Framework 时遇到过哪些问题,以及如何以一种安全的方式使用这个框架。

背景

在开始之前,我假设我们对 Entity Framework Code First 有一些了解。至少,我们知道如何在 Entity Framework 中创建表、关系以及使用种子数据。

如果我们不了解,开始也不会太难

或者只是谷歌搜索一段时间,也就可以了。

那么,Entity Framework Code First 需要什么?

  1. 模型:将它们转换为表...
  2. 模型的配置:创建表之间的关系,以及格式化表列结构...
  3. 上下文:表示数据库、连接、调用模型配置、数据库初始化、数据库加载配置...
  4. 初始化程序:初始化数据库上下文,使用种子...
  5. 迁移:如果模型发生变化,如何处理旧数据或结构...

你提到的那些问题是什么?

问题开始于我们误用了以下部分:

  1. 数据库上下文的初始化
  2. 处理迁移

让我们来解释这些部分。

1. 数据库上下文的初始化

要初始化数据库上下文,我们可以使用 IDatabaseInitializers 类,例如:

CreateDatabaseIfNotExists – 首先创建数据库,如果应用程序没有可用的数据库,由上下文和连接字符串指定(适用于实时项目)

但我的主机提供商不允许我这样做。它要求我手动创建数据库,然后再使用。我该怎么办?

更新的需求迫使你向表中添加一个新字段。你做到了,并提供了更新的上下文。现在如果我们运行应用程序,就会抛出一个异常,指向当前上下文和现有数据库不匹配。我该怎么办?

DropCreateDatabaseIfModelChanges – 如果数据库与更新的上下文不匹配,则删除旧数据库,并创建一个新的数据库,因为上下文指向它(适用于开发)

更新的需求迫使你向表中添加一个新字段。你做到了,并提供了更新的上下文。现在如果我们运行应用程序,旧数据库将被删除。所有数据都会丢失。我该怎么办?

DropCreateDatabaseAlways - 每次初始化上下文时,都会删除旧数据库并创建一个新数据库。(适用于测试,如果数据库容量较小。)

我们将无法存储足够的数据来工作。如果应用程序是实时操作的,我们绝不应该使用它。我该怎么办?

所以有人说,为什么不为实时项目使用 CreateDatabaseIfNotExists,为开发使用 DropCreateDatabaseIfModelChanges,并使用 SQL 脚本来创建数据库?当我们上线时,将 DropCreateDatabaseIfModelChanges 替换为 CreateDatabaseIfNotExists,问题就解决了。如果我们某天提供更新,但忘记这样做怎么办?

2. 处理迁移

迁移确实是使用 Entity Framework Code First 的好方法。它使数据库能够保存信息,例如进行了多少次迁移以及何时进行的。此外,在迁移文件中,我们可以找到对迁移所做的更改,甚至可以在迁移之间来回跳转。在下一篇文章中,我们将讨论它。

但问题是,配置起来有点棘手。如果你没有多少时间进行研究,迁移可能会迫使你放弃使用 Entity Framework Code First 的选项。因为有时,应用程序会随着时间的推移而变得庞大,这迫使我们更改数据库数据和表关系等。

人们不喜欢为大型项目冒险。他们希望像以前使用 SQL 脚本那样处理事情。

解决方案是什么?

这是关键:Entity Framework 也可以用来将上下文映射到现有数据库。那么我们将做什么?

  1. 使用一个用于数据库的生成器上下文,它使用 DropCreateDatabaseIfModelChanges。该上下文的访问仅限于特定项目,而不包括解决方案中的其他项目。或者可以被其他项目的代码继承(使用 internal/ protected internal 作为访问修饰符)。这将用于重建数据库。
  2. 使用一个用于数据库的映射器上下文,它仅映射到数据库实体。这主要将由应用程序用来影响表。
  3. Base 上下文继承通用内容,用于字段表和配置文件

映射器上下文

映射器上下文很简单,它继承了基础 Context 类。

我们将使用此上下文进行任何 CRUD 操作。

/*Context is only mapped to bd objects*/
public class UmsContext : Context
{
    public UmsContext() : base("DbUms")    //connection string name to base          
    {
        /*Configuration for the context*/
        Configuration.ProxyCreationEnabled = false;
        Configuration.LazyLoadingEnabled = false;
    }
}

生成器上下文

生成器上下文也继承自基础 Context 类,其初始化程序如下:

我们将仅使用此类来重新创建数据库

/*Context drops the old database and creates new one*/
internal class UmsBuildContext : Context
{
    static UmsBuildContext()
    {
        //check connection string
        string connectionString = ConfigurationManager.ConnectionStrings["DbUms"].ConnectionString;
        Database.SetInitializer(new UmsBuildContextInitializer());
    }

    public UmsBuildContext()
        : base("DbUms") //connection string name to base
    {
    }
}

初始化程序也可以像这样包含种子:

internal class UmsBuildContextInitializer
    : DropCreateDatabaseIfModelChanges<UmsBuildContext>
{
    protected override void Seed(UmsBuildContext context)
    {
        /*department seeds*/
        List<Department> departments = new List<Department>()
        {
            new Department(){Name = "Math"},
            new Department(){Name = "Physics"},
            new Department(){Name = "Chemistry"},              
        };
        context.Departments.AddRange(departments);
        context.SaveChanges();

        /*students seeds*/
        List<Student> students = new List<Student>()
        {
            new Student(){ Name = "Nitol", DepartmentId = 1},
            new Student(){ Name = "Tasnim", DepartmentId = 2},
            new Student(){ Name = "Ripon", DepartmentId = 3}
        };
        context.Students.AddRange(students);
        context.SaveChanges();
    }
}

正如我们所说,我们想限制生成器上下文。这就是为什么我们使用 internal 作为 UmsBuildContext 的访问修饰符。但为了在测试项目中使用此上下文来重新创建数据库以进行测试/故意操作,也需要这样做。因此,为了访问其部分功能,我们在 public 类中使用此类,例如:

public class DbBuilder
{
    public void Build()
    {
        var departments = new UmsBuildContext().Departments.ToList();
    }
}

基类上下文

主要的基类上下文主要保存所有表配置和数据库实体引用,而上述上下文继承自它。

public class Context : DbContext
{
    /*all tbls*/
    public DbSet<Department> Departments { get; set; }
    public DbSet<Student> Students { get; set; }

    /*connection string provided by the sub class*/
    protected Context(string connectionString)
        : base(nameOrConnectionString: connectionString)
    {
    }

    /*get tbl by tbl type*/
    public DbSet<TSource> Table<TSource>() where TSource : class
    {
        return Set<TSource>();
    }

    /*all tbls configurations*/
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new DepartmentConfiguration());
        modelBuilder.Configurations.Add(new StudentConfiguration());
    }
}

Using the Code

因此,如果数据库结构发生任何更改,并且我们想重建整个数据库,我们只需调用:

/*Rebuild the db*/
//use only when we want to rebuild the database with seed data during production.
//avoid any use when the product is already been delivered to client.(best use in test code)
new DbBuilder().Build();

现在,让我们测试映射器上下文

/*see the datas*/
var db = new UmsContext();
var departments = db.Departments.ToList();
var departments2 = db.Table<Department>().ToList();

/*add new student with new department*/
var student = new Student()
{
    Name = "Rintu",
    Department = new Department() {Name = "CSE"}
};
db.Students.Add(student);
db.SaveChanges();

在这里,数据库中将创建新的 studentdepartment 行,这意味着映射器工作正常。

更改数据库结构

为此,我们只需要:

  1. 修改模型类
  2. 在本地机器上重建数据库
  3. 将重新创建的数据库与旧数据库进行比较,以找到将旧数据库更新到当前版本的必需的 alter/update SQL 查询,并将这些查询应用于旧数据库。
  4. 用新创建的 DLL 替换旧的 DLL

限制

  1. 是的,可能会有一些我还没有遇到的错误,或者可能造成一些道德上的困扰。所以如果你发现任何问题,请告诉我。
  2. 我并非不鼓励任何人使用迁移。如果你有时间,请做一些研发。
  3. 重建系统可能看起来有点棘手,但我正在努力使其更合乎逻辑。
public class DbBuilder
{
    public void Build()
    {
        var departments = new UmsBuildContext().Departments.ToList();
    }
}

或者看看这个:http://stackoverflow.com/questions/4911329/ef-codefirst-ctp5-manually-drop-and-create-db

在附件中找到适用于框架 4 项目的 Visual Studio 2010 解决方案。如果你运行该项目,它将在本地机器上创建一个名为 "UMS" 的数据库,其中包含 3 个表。你可以通过 app.config 进行更改。

© . All rights reserved.