Windows Phone Local Database Schema Upgrade Part1 - Adding new columns

Linq To SQLCE 可以对独立存储中的SQLCE进行基本的数据操作,但是,如何在已存在的数据库表中新建数据列,下文提供了非常优雅的解决方案。

In this article I am going to talk about how to update your database schema when updating your app. The problem is that if you change the database schema(for.ex: add new columns or tables) in a future version of your app, then when users that use the old version of your app update to the new one an exception occurs. So in practice if there is no additional code to change the database so that it is compatible with the latest version of the app the following exception occurs:(本文讨论如何升级app的数据库,问题的关键在于如果在未来版本的应用中更改了数据库,那么老版本的应用的用户升级到新版本是,那么新版应用在访问旧版数据库时,就要产生异常。)

image

So in this series of posts I will explain how to prevent this exception from happening. (本系列将讨论如何阻止异常出现。)

开始之前,首先新建一个wp应用程序,并按以下步骤操作。

Step1. Add a new class Person to the project with the following properties:(新建一个Person类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[Table(Name = "People")]
public class Person
{
    [Column(IsPrimaryKey = true, IsDbGenerated = true)]
    public int ID
    {
        get;
        set;
    }
 
    [Column(CanBeNull = false)]
    public string FirstName
    {
        get;
        set;
    }
 
    [Column(CanBeNull = true)]
    public string LastName
    {
        get;
        set;
    }
 
    [Column(CanBeNull = false)]
    public int Age
    {
        get;
        set;
    }
    public override string ToString()
    {
        return string.Format("{0} {1}, Age: {2}",
            this.FirstName, this.LastName, this.Age);
    }
}

Step2. Next add a new class PeopleDataContext to the project which will be our data context:(新建PeopleDataContext)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class PeopleDataContext : DataContext
{
    public PeopleDataContext(string connectionString)
        : base(connectionString)
    {
    }
 
    public Table<Person> People
    {
        get
        {
            return this.GetTable<Person>();
        }
    }
}

 

Step3. Next we will add a new button that will be used to initialize the database.(增加一个按钮来初始化数据库)

1
2
3
4
5
6
7
8
9
10
11
12
private void btnInitialize_Click(object sender, RoutedEventArgs e)
{
    using (PeopleDataContext context = new PeopleDataContext(ConnectionString))
    {
        if (!context.DatabaseExists())
        {
            // create database if it does not exist
            context.CreateDatabase();
            this.WritePeople(context);
        }
    }
}

Where WritePeople is the following method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void WritePeople(PeopleDataContext context)
{
    for (int i = 0; i < 10; i++)
    {
        Person person = new Person()
        {
            FirstName = string.Format("FirstName#{0}", i),
            LastName = string.Format("LastName#{0}", i),
            Age = i
        };
        context.People.InsertOnSubmit(person);
    }
    context.SubmitChanges();
}
1
2
3
4
5
6
7
public void WritePeople()
{
    using (PeopleDataContext context = new PeopleDataContext(ConnectionString))
    {
        this.WritePeople(context);
    }
}

Step4. We will add one more button that we will use to read the data from the database(this is only for  testing purposes to prove that the database is functioning properly):(再添加一按钮,读取数据库)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void btnReadPeople_Click(object sender, RoutedEventArgs e)
{
    this.lbPeople.ItemsSource = this.ReadPeople();
}
 
public IEnumerable<Person> ReadPeople()
{
    IEnumerable<Person> people = null;
    using (PeopleDataContext context = new PeopleDataContext(ConnectionString))
    {
        people = context.People.ToList();
    }
 
    return people;
}

NOTE: All that the above code does is read the Person records from the database and show them in a ListBox.(上述代码仅是读取Person记录并显示在ListBox)

Step5. If we run the application now and press the initialize button and after that the read people button we should see the following screen.I.e. this is the first version of our database: (运行程序,依次点击initialize database、Read People,应该可以看到如下画面)

image

Step6. For the next version of our app we will update the Person class with two additional properties:(在Person类中,增添两个属性)

NOTE: It is important to notice that in the snippet below  the Address and the Email properties are added in the second version of the schema.(注意:Address & Email被添加了进来,友情提示,请注意预编译命令的使用。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[Table(Name = "People")]
public class Person
{
   //...
 
#if DB_VERSION_1
    [Column(CanBeNull = true)]
    public string Address
    {
        get;
        set;
    }
 
    [Column(CanBeNull = true)]
    public string Email
    {
        get;
        set;
    }
#endif
 
    public override string ToString()
    {
#if DB_VERSION_1
        return string.Format("{0} {1}, Age: {2}, Email: {3}",
            this.FirstName, this.LastName, this.Age, this.Email);
#else
        return string.Format("{0} {1}, Age: {2}",
            this.FirstName, this.LastName, this.Age);
#endif
    }
}

NOTE: We will use the DB_VERSION_1 conditional compilation symbol in order to turn on code related to the second version of the database schema. (使用DB_VERSION_1条件编译符号来表明数据库的版本变化)

To turn the sections of the code marked with the DB_VERSION_1 symbol just add it to the conditional compilation symbols in the project`s Build properties:(DB_VERSION_1 的使用方法如下图所示)

image

Step7. If we were to run the application now without writing any additional code we will get the following exception:(工作未完,现在运行依然会得到如下异常)

image

To prevent this from happening we will add some code to update the database schema to include the Address andEmail columns. Here is how our initialize database method should look like now:(因此,我们必须在旧版的数据库中添加Address & Email 列)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void btnInitialize_Click(object sender, RoutedEventArgs e)
{
    using (PeopleDataContext context = new PeopleDataContext(ConnectionString))
    {
        if (!context.DatabaseExists())
        {
            // create database if it does not exist
            context.CreateDatabase();
            this.WritePeople(context);
        }
        else
        {
            // create an instance of DatabaseSchemaUpdater
            DatabaseSchemaUpdater schemaUpdater = context.CreateDatabaseSchemaUpdater();
            // get current database schema version
            // if not changed the version is 0 by default
            int version = schemaUpdater.DatabaseSchemaVersion;
 
            // if current version of database schema is old
            if (version == 0)
            {
                // add Address column to the table corresponding to the Person class
                schemaUpdater.AddColumn<Person>("Address");
                // add Email column to the table corresponding to the Person class
                schemaUpdater.AddColumn<Person>("Email");
                // IMPORTANT: update database schema version before calling Execute
                schemaUpdater.DatabaseSchemaVersion = 1;
                // execute changes to database schema
                schemaUpdater.Execute();
            }
        }
    }
}

What will happen when the application is ran is the following:

  • for an updated application, since the database probably already exists the code in the else branch will execute. Here we create a new instance of the DatabaseSchemaUpdater class. Then we retrieve the current version of the database schema and if it is the older version we update the schema with the new columns, increase the schema version and execute the schema changes. (对于更新升级的应用,由于数据库已经存在,那么else分支将被执行,我们只用DatabaseSchemaUpdater的一个实例,来获取当前数据库版本,如果是旧版的数据库,添加相关数据列,并修改数据库版本号)
  • for a newly installed application a new database will be created (对于全新安装的应用,只需新建数据库即可)

Step8. To verify that the schema upgrade code works properly we will add a new button and will use the new properties added with the second version of the database schema:(验证数据库是否已更新)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void btnUpdatePeople_Click(object sender, RoutedEventArgs e)
{
#if DB_VERSION_1
    using (PeopleDataContext context = new PeopleDataContext(ConnectionString))
    {
        // for version 1 - update people records with email address
        List<Person> people = context.People.ToList();
        int count = people.Count;
        for (int i = 0; i < count; i++)
        {
            Person person = people[i];
            person.Email = string.Format("person{0}@domain.com", i);
        }
        context.SubmitChanges();
    }
#endif
}

Step9.Finally if we run the application and press the Initialize, Update and Read buttons we should see the following screen:

image

That`s it for now. In the next article we will discuss more complex changes to the database schema. Stay tuned.

You may also find helpful the following articles:

Here is the full source code:

Hole the post was helpful.

 

You can also follow us on Twitter @winphonegeek

posted @ 2012-04-11 15:17  逝 者 如 斯  阅读(341)  评论(0编辑  收藏  举报