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

DevForce Code First 演练:从新项目到运行

starIconstarIconstarIconstarIconstarIcon

5.00/5 (6投票s)

2011年11月17日

CPOL

8分钟阅读

viewsIcon

36575

downloadIcon

296

本教程演示如何使用 DevForce 中的 Code First 构建一个简单的 WPF 应用程序。

引言

Code First 演练示例向您展示如何从头开始构建 DevForce Code First WPF 应用程序。

背景

应用程序本身不是重点。我们不致力于制作一个有用的应用程序,也不演示 UI 设计或开发中的最佳实践。我们的目的是引导您完成一系列步骤,介绍以 DevForce Code First 风格编写和使用实体模型的基本要素。

本教程演示如何使用 Code First 构建一个简单的 WPF 应用程序。您将定义一个单实体(Category)模型,向开发数据库添加其实例,查询它们,将它们格式化为string,并将其作为日志消息显示在屏幕上。

DevForce 实现了一个端到端的多层架构。它通过在服务器上公开一个通用 Web 服务来实现,该服务接收 LINQ 表达式并通过互联网将业务对象返回给客户端。

DevForce 为您处理所有多层 WCF 通信、序列化和编组。

剧透警告:所有 CF 实体都是 DevForce 实体,并且与从 EDMX 生成的 DevForce 实体具有所有相同的优势。

开始之前

DevForceEntity Framework 4.1 必须已安装。

本演练使用 SQL Server Express,它直接与 Entity Framework 的 Code First 功能集成。如果您想使用非 Express 版本的 SQL Server,请参阅此部分:摆脱 SQL Server Express

创建新项目

  1. 文件 | 新建 | 项目 | DevForce 2010 | DevForce WPF 应用程序
  2. 将其命名为 CodeFirstWalk

MainWindow.xaml:我们初尝 WPF UI

  1. 打开 MainWindow.xaml。确保 XAML 窗格已显示。
  2. 将窗口加宽。Width=800 即可。
    Title="MainWindow" Height="350" Width="800">
  3. 添加一个两行网格,第一行是标题,第二行是一个 ListBox 用于显示消息。我们立即使用数据绑定,将 ListBoxItemSource 绑定到一个名为 Messages 的集合属性。
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="Auto" />
         </Grid.RowDefinitions>
        <TextBlock Text="Code First Walk in WPF"
                   FontSize="20" TextAlignment="Center" VerticalAlignment="Center"/>
        <ListBox x:Name="messages" Grid.Row="1" ItemsSource="{Binding Messages}" />      
    </Grid>
  4. 关闭所有窗口(Alt-WL)。

我们很快就会回到 UI。

添加 DevForce CF 标记文件

  1. 添加 | 新建项 ([Ctrl+Shift+A]) | DevForce 2010 | DevForce CodeFirst 文件
  2. 这还将添加我们需要的 CF 库。
  3. 将其命名为 DevForce.cf(尽管名称无关紧要)。
  4. 关闭编辑器窗口。

创建模型

  1. 添加 | 新建项 | 类.
  2. 将其命名为 Model
  3. 删除模板生成的 Model 类。
  4. 在下方添加 Category 类。
[ProvideEntityAspect]
public class Category
{
   public int CategoryId { get; set; }
   public string CategoryName { get; set; }
} 

ProvideEntityAspect 属性要求我们添加 using IdeaBlade.Aop;

添加自定义 EntityManager

我们将从在同一个 Model.cs 文件中创建它开始。

欢迎您随时将这些类中的任何一个或所有类移动到它们自己的类文件中。

  1. 在文件顶部,定义以下自定义 ProductEntities EntityManager 类。
    public class ProductEntities : EntityManager {}

    EntityManager 类需要使用 IdeaBlade.EntityModel;

  2. 添加 Categories EntityQuery 属性,以便我们更容易构建 Category 实体的查询。
    public EntityQuery<Category> Categories { get; set; }

构建解决方案

  1. 构建解决方案 (Ctrl-Shift-B)。这是使用 Code First 模型进行的首次构建。(如果您收到构建错误,指示未安装 SQL Server Express,请参阅摆脱 SQL Server Express)。

    请注意,它会在项目中生成 ProductEntities.ibmmx

  2. 查看输出窗口中的构建输出。

    在更改模型后,您应始终进行构建,以确认 DevForce(重新)生成了元数据 .ibmmx 文件。

您是否收到了提及 Microsoft SQL Server Express 的构建错误?如果是,请参阅下面的“构建错误”部分。

创建 ViewModel

是的,我们将在此示例中使用 MVVM 模式。这非常简单。

  1. 添加 | 新建项 | 类。
  2. 将其命名为 MainWindowViewModel
  3. 使其成为 public 类。
  4. 添加一个无参数构造函数。
  5. 调用以下四个方法
    Start();
    SetManager();
    AddTestData();
    ShowTestData();
  6. 让 Visual Studio(或 R#)为这些方法创建存根(从下往上工作,以便存根从上往下创建)。

Start()

  1. 删除抛出 NotImplementedException 的行。
  2. 初始化一个 Messages 集合属性来保存我们的 string 消息(回想一下在 UI 中,我们说过要绑定到它)。
    Messages = new ObservableCollection<string>();

    ObservableCollection 类需要使用 System.Collections.ObjectModel;

  3. 接下来调用我们很快就会编写的 Log(…) 方法
    Log("Starting application");
  4. 让 Visual Studio(或 R#)为 Log 方法创建存根。
  5. Start 方法正下方添加一个 public Messages 自动属性。
  6. 此时,此部分看起来像这样
    private void Start()
     {
         Messages = new ObservableCollection<string>();
         Log("Starting application");
     }
    
    public ObservableCollection<string> Messages { get; set; }
    
    private void Log(string startingApplication)
     {
        throw new NotImplementedException();
     }
  7. 实现 Log 方法,使其将带编号的消息插入到 Messages 集合的前面。这种策略使得最新的消息显示在消息 ListBox 的顶部。实现如下
    private void Log(string message)
    {
        message = String.Format("{0}: {1}", ++_messageCounter, message);
        Console.WriteLine(message);
        Messages.Insert(0, message); // new messages in front
    }
    private int _messageCounter; 
  8. 我们还将日志输出到 Visual Studio 控制台窗口。

SetManager()

我们在此处创建并初始化您的自定义 EntityManager

我们更喜欢将 EntityManager 的创建以及此处将看到的查询和保存逻辑放在存储库 (Repository) 或 DataServices 类中。在此演示中不这样做;那是未来的一项练习。

  1. 删除抛出 NotImplementedException 的行。
  2. 实例化您的 ProductEntities 类并将其分配给 Manager 属性。
  3. SetManager 方法下方添加一个私有 Manager 自动属性。

    服务器可能并不总是那么友好。服务器上抛出的异常将发送到客户端,并在此处以 EntityServerException 的形式出现。我们可以在此处捕获它们,通过 EntityServerError 事件处理程序来:

    1. 表示我们正在处理错误,
    2. 记录问题,以及
    3. 撤消 EntityManager 中可能导致问题的任何待定更改。
  4. 我们可以在 lambda 中定义处理程序。结果如下
    private void SetManager()
    {
        Manager = new ProductEntities();
        Manager.EntityServerError += (s, e) =>
        {
            e.Handled = true; // we're dealing with it here
           Log("Server error: " + e.Exception.Message);
            Manager.RejectChanges(); // undo pending changes
       };
    }
    
    private ProductEntities Manager { get; set; }
  5. 您可能希望在 lambda 表达式的第一行添加一个断点,以防我们遇到错误并希望查看整个异常。

AddTestData()

  1. 删除抛出 NotImplementedException 的行。
  2. 创建一个新的 Category 实体并设置其名称。
    var cat = new Category 
        {CategoryName = "Sweet Things on " + DateTime.Now.ToString("o")}; 
  3. 将其添加到 Manager,记录我们正在保存的事实,并调用 SaveChanges
    Manager.AddEntity(cat);
    Log("Saving new test data");
    Manager.SaveChanges(); 
  4. AddTestData 方法应如下所示
    private void AddTestData()
     {
        var cat = new Category { CategoryName = "Sweet Things on " + 
            DateTime.Now.ToString("o") };
        Manager.AddEntity(cat);
        Log("Saving new test data");
        Manager.SaveChanges();
    
     } 

ShowTestData

  1. 删除抛出 NotImplementedException 的行。
  2. 我们将记录显示测试数据的事实
    Log("Showing test data");
  3. 查询所有 Category 实体。这将包括我们刚刚添加的实体以及之前应用程序会话中数据库中潜藏的任何其他实体。
    添加此查询
    var cats = Manager.Categories.ToList(); // get 'em all
  4. 使用辅助方法记录我们获取的类别
    cats.ForEach(LogCats);

    ToList()ForEach() 需要使用 using System.Linq;

  5. 让 Visual Studio(或 R#)为 LogCats 方法创建存根。
    • obj 参数名称更改为 cat
    • 设置格式 stringvar fmt = "Category {0} Name={1}";
    • 调用 Log:Log(string.Format(fmt, cat.CategoryId, cat.CategoryName));

    ShowTestDataLogCats 方法应如下所示

    private void ShowTestData()
     {
         Log("Showing test data");
         var cats = Manager.Categories.ToList(); // get 'em all
        cats.ForEach(LogCats);
     }
    
    private void LogCats(Category cat)
     {
         var fmt = "Category {0} Name={1}";
         Log(string.Format(fmt, cat.CategoryId, cat.CategoryName));
     } 

在视图中创建 ViewModel

是时候将 View (MainWindow.xaml) 与 ViewModel (MainWindowViewModel.cs) 绑定了。我们将在视图的代码隐藏中完成此操作。

这通常不是最佳选择,但对于我们的演示来说已经足够了。

  1. 打开 MainWindow.xaml.cs
  2. 删除除 System.Windows 之外的所有 using(可选)。

在构造函数中,将视图的 DataContext 赋值为 MainWindowViewModel ViewModel 类的新实例。完成后,构造函数如下所示。

public MainWindow()
{
    InitializeComponent();
    DataContext = new MainWindowViewModel();
}

在调试模式下运行 [F5]

它再次构建。这次输出窗口中的构建消息如下所示

Model metadata created for ProductEntities<br />
Model metadata for ProductEntities.ibmmx is unchanged

元数据 ibmmx 文件未更改。但程序集已更改……因为现在该元数据文件已嵌入到 CodeFirstWalk.exe 程序集中。

应用程序运行。而且它成功了!

消息按倒序自上而下显示。顶部是我们刚刚创建的 Category

再次运行应用程序,您将在应用程序窗口中看到两个 Categories,一个是我们在上一个会话中创建的,另一个是我们刚刚添加的新类别。

这怎么可能?我们还没有定义数据库。我们甚至没有命名数据库。

在第一次会话中,当 AddTestData() 方法要求 EntityManager 保存新的 Category 时,EntityManager 发出了第一个需要数据库的命令。在随后的一系列事件中,DevForce 将连接字符串的名称提供给 Entity Framework。在我们的示例中,它提供了 EntityManager 类的名称 (ProductEntities)。

因为 app.config 文件中没有 ProductEntities 连接字符串,所以 Entity Framework Code First 会在 SQL Server Express 中创建一个与我们实体模型匹配的数据库,并将其命名为 ProductEntities

就目前而言,您必须安装并运行 Microsoft SQL Server Express,否则 Entity Framework 无法为您创建缺失的数据库。您可以更改该默认设置

在 SQL Server Management Studio 中检查数据库

数据库存在,并且有一个“Categories”表,其中包含 UI 中显示的两行。

摘要

在 Code First 演练的这一部分,我们

  • 创建了一个新的 DevForce WPF 应用程序
  • 使用“Code First”完全在代码中定义了 DevForce 实体模型
  • 将我们的实体访问代码放入一个简单的 ViewModel 中,并将该 ViewModel 绑定到 View。
  • 添加了新实体,保存了它们,并从数据库中查询回它们。
  • 让 Entity Framework 创建一个与我们的 Model 匹配的数据库。

附录:常见构建错误

您收到了如下构建错误

生成元数据时发生错误,未能创建元数据文件。错误:未找到 connectionString。由于未安装 SQLExpress,DevForce 元数据发现无法继续。请向此项目添加一个包含名为“ProductEntities”的 connectionString 的 .config 文件。在构建时提供连接字符串可确保生成正确的模型元数据。

Entity Framework 默认期望您已安装 Microsoft SQL Server Express。您可以通过在配置文件中指定连接字符串或通过设置 DefaultConnectionFactory 将默认数据库服务器更改为 SQL Server 来解决此问题。这些选项在 DevForce 资源中心的高级数据库连接选项主题中讨论。

如果您没有 SQL Server Express,但安装了完整的 SQL Server,请尝试将以下 static 构造函数添加到 ProductEntities。请记住仔细检查 baseConnectionString 的各个部分,以确保它们与您的 SQL Server 名称匹配。

public class ProductEntities : EntityManager
{
   static ProductEntities()
    {
       // Set base connection string
       const string baseConnectionString =
       "Data Source=.; " +  // your SQL Server name
       "Integrated Security=True; " +
       "MultipleActiveResultSets=True; " +
       "Application Name=CodeFirstWalk"; // change to suit your app

        Database.DefaultConnectionFactory = 
        new SqlConnectionFactory(baseConnectionString);
    }

   // elided.
} 

历史

  • v.1:原始版本
  • v.2:根据评论线程中的问题添加了有关 DevForce 的信息
  • v.3:更新了开始之前部分,明确指出必须安装 DevForce 和 EF 4.1
© . All rights reserved.