HR 技能应用程序代码(EDM 示例应用程序)

HRSkills 应用程序使用的代码演示了 实体数据模型 (EDM) 的多项功能。之前的 HRSkills 主题中描述的架构是本节代码使用的实体和关联的基础。有关本示例中的实体和关联的信息,请参见人力资源技能 WinApp(EDM 示例应用程序)。有关存储元数据的信息,请参见 HR 技能存储元数据(EDM 示例应用程序)。有关映射规范,请参见 HR 技能映射规范(EDM 示例应用程序)

架构中设计并映射到存储的类型将生成为可编程对象模型。此模型上的数据可使用公共语言运行库 (CLR) 语法(无需凭借以字符串形式嵌入代码的 SQL 查询)编程。

此应用程序演示到实体的数据绑定、参数化查询以及关联上的导航属性的用法。关联将 EmployeesReferencesEmployeesSkills 以及 Skills 与包含技能相关信息的 SkillInfo 实体连接在一起。

配置文件和连接字符串

使用该对象模型需要连接到存储应用程序数据的数据库。还需要指向由架构生成的 DLL 提供的运行时对象的实体连接。有关从架构生成对象模型的信息,请参见“如何:如何:使用 EdmGen.exe 生成实体数据模型(实体框架)”。

exe.config 文件中包含用于连接到 SQL Server 数据库并建立实体连接的连接字符串。实体连接就位后,即可从代码访问对象模型中的实体和关联。

开发人员必须将连接字符串文本添加到 exe.config 文件。此应用程序指定类 HRSkills。赋值语句 providerName="System.Data.EntityClient" 指定了一个实体连接,该连接使用 HR 技能映射规范(EDM 示例应用程序)中定义的映射架构。

连接字符串还标识 SQL 连接所用的服务器:provider connection string="server=servername;

下面的示例演示 exe.config 文件的内容。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    
  <connectionStrings>
    <add name="HRSkills" 
         connectionString='metadata=.;
         provider=System.Data.SqlClient;
         provider connection string="server=serverName;
         database=HRSkills;
         integrated security=true;
         multipleactiveresultsets=true"'
         providerName="System.Data.EntityClient"/>
  </connectionStrings>
</configuration>

应用程序代码

下面的代码包含从 Windows 窗体中初始化并运行的常规事件处理程序,该 Windows 窗体用作本示例应用程序中的用户界面。预处理器 using 指令包含执行用于查找员工、推荐人、技能和技能信息的查询所需的命名空间。最后一条 using 指令中的命名空间 HRSkillsModel 包含用于为本应用程序定义和生成的对象模型中的实体和关系的运行时类,如本节中前面的主题所述。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Data.Mapping;
using System.Data.Objects;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data.Metadata;
using System.Linq;
using HRSkillsModel;

namespace HR_Skills_WinApp
{
    public partial class Form1 : Form
    {
        HRSkills DB = new HRSkills();
        
        public Form1()
        {
            InitializeComponent();

声明并初始化 ObjectContext 的变量:HRSkills DB = new HRSkills。连接将包含在 ObjectContext 中,并在 try 块中初始化,如下面的代码段所示。可在测试应用程序时读取任何连接错误消息。

用于显示存储中的所有 Employees 的初始查询使用 Employees 实体。Employees 类是 ObjectContext 中返回所有员工实体的 ObjectQuery<T>

DataGridView 控件用于显示 Employees 查询的结果。列添加到 DataGridView 中。BindingSource 初始化为 Employee 的 Query<T>,该控件的 DataSource 属性将绑定数据。在 DataGrid 控件中进行编辑后使用参数 true 更新数据库。

        public Form1()
        {
            InitializeComponent();

            try
            {
                bindingSource1.DataSource = DB.Employees;
                dataGridView1.DataSource = bindingSource1;
                dataGridView1.Columns[0].Visible = false;
            }
            catch(Exception e)
            {              
                System.Windows.Forms.MessageBox.Show(e.ToString());
            }
        }

DataGridView 控件中显示 Employees 以后,应用程序用户可以单击任何一行获取与该行中显示的 Employee 实体关联的 Skills。新的查询将查找其数据显示在 DataGridView 中的 Employee 实体的 EmployeeIdDataGridView 显示中隐藏了 EmployeeId,但将其保留用于引用目的。它由 Employees 上的参数化 ObjectQuery<T> 使用。该参数在 ObjectParameter param = new ObjectParameter("p", empId) 一行中进行创建和初始化。在以下查询中使用了 ObjectParameterDB.Employees.Where("it.EmployeeId = @p", param).First()

该查询标识并返回 employee 类后,将使用 Skill_Employee 关联查找与此 employee 关联的所有 Skills。以下一行代码使用 employee 的导航属性加载数据库中与该 employee 关联的所有 SkillsSkill_Employee.GetSkillsEntities(employee).Load()。接着,同一导航属性上的 foreach 循环将与该 employee 关联的 Skills 读入另一个 DataGridView 控件。

        private void dataGridView1_CellClick(object sender, 
            DataGridViewCellEventArgs e)
        {
            dataGridViewSkills.Columns.Clear();

            // Get the Id of the Employee and 
            Guid empId = new Guid(dataGridView1.CurrentRow.Cells[0].Value.ToString());

            // Find the Employee.
            ObjectParameter param = new ObjectParameter("p", empId);

            try
            {
                    if (null != DB.Employees.Where(
                        "it.EmployeeId = @p", param).First())
                    {

                        Employees employee = DB.Employees.Where(
                        "it.EmployeeId = @p", param).First();
                
                        employee.Skills.Load();
                        List<Skills> skillsList = new List<Skills>();
                        foreach (Skills skill in employee.Skills)
                        {
                            skillsList.Add(skill);
                        }

                        bindingSource2.DataSource = skillsList;
                        dataGridViewSkills.DataSource = bindingSource2;

                        dataGridViewSkills.Columns[0].Visible = false;
                        dataGridViewSkills.Columns[2].Width = 300;

                
                        richTextBox1.Clear();

                        // Provide EmployeeId for new skill or 
                        // reference association.
                        textBoxSkillEmployeeId.Text = 
                            employee.EmployeeId.ToString();

                        textBoxSkillEmployeeAlias.Text = employee.Alias;

                        textBoxRefEmployeeId.Text = 
                            employee.EmployeeId.ToString();

                        textBoxRefEmployeeAlias.Text = employee.Alias;
                    }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.InnerException.ToString());
            }

        }

下一方法在用户界面上的一个 RichTextBox 控件中显示 SkillInfo 实体包含的有关 Skills 的信息。将使用与上一方法相同的过程查询和显示与 Skills 实体关联的 SkillInfo 实体。一个查询使用 SkillId 作为参数,查找特定的 Skills 实体。对 Skills 实体的导航属性进行导航,以查找与该 skill 关联的所有 SkillInfo 实体。

SkillInfo 实体的属性包含 URL,这些 URL 用于定位与关联 Skills 有关的其他信息。每个 SkillInfo 实体中的 URL 都显示在格式文本框中。

在此方法中使用的与 Skills 实体关联的 employee 实体还可以通过 Skill_Employee 关联从 Skills 实体访问。使用以下一行代码从数据库加载 employee 的推荐人:Skill_Employee.GetEmployeesRef(skill).Load()。在另一行中将 employee 赋值给变量:Employees employee = Skill_Employee.GetEmployees(skill)。该 employee 的别名被写入格式文本框显示中。

将从 Reference_Employee 关联获取与该 employee 关联的推荐人。References 实体的属性包含所有推荐人的姓名、职位和电子邮件地址。这些信息显示在 Rich Text Box 中,用于补充指向员工技能相关信息的链接。

        private void dataGridViewSkills_CellClick(object sender, 
            DataGridViewCellEventArgs e)
        {
            richTextBox1.Clear();
            // Write the name of the skill and brief 
            // description to richtext box.
            richTextBox1.Text = richTextBox1.Text + "Skill Name: "
                + dataGridViewSkills.CurrentRow.Cells[1].
                Value.ToString() + "\n" + 
                dataGridViewSkills.CurrentRow.Cells[2].
                Value.ToString();

            // Create ObjectParameter from the SkillId property 
            // and get the Skill.
            Guid skillId =
     new Guid(dataGridViewSkills.CurrentRow.Cells[0].Value.ToString());

            ObjectParameter param = 
                new ObjectParameter("p", skillId);

            Skills skill = DB.Skills.Where("it.SkillId = @p", 
                param).First();

            // Load the SkillInfo entities using 
            // SkillInfo_Skill association.
            skill.SkillInfo.Load();
            foreach (SkillInfo skillInfo in skill.SkillInfo)
            {
                richTextBox1.Text = richTextBox1.Text +
                    "\n\nSkill Information: " + skillInfo.URL + 
                    "\n";
            }

            dataGridView1.ClearSelection();

            // Load the Employee associated with the 
            // Skill using Skill_Employee association.
            skill.EmployeeReference.Load();
            Employees employee = skill.Employee;
            if (null == employee) return;

            // Write the alias property of the Employee to rich text 
            // box and heading for references.
            richTextBox1.Text = richTextBox1.Text + 
                "\n\nEmployee: " + employee.Alias + 
                "\n\n" + "References:";

            // Load References of Employee using 
            // Reference_Employee association.
            employee.References.Load();

            foreach (References reference in employee.References )
            {
                // Write reference LastName and Position to 
                // richtext box.
                richTextBox1.Text = richTextBox1.Text + "\n" +
                    reference.FirstName + " " + reference.LastName + 
                    "  Position: " + reference.Position + 
                    "  Email: " + reference.Email;
            }

            // Provide SkillId for new SkillInfo if needed.
            textBoxSkillInfoSkillId.Text = skill.SkillId.ToString();

            for (int i = 0; i < dataGridView1.RowCount; i++)
            {
                // Check to see if this is the employee associated
                // with the skill.
                if (dataGridView1.Rows[i].Cells[0].Value.ToString()
                    == employee.EmployeeId.ToString())
                {
                    dataGridView1.Rows[i].Selected = true;
                    dataGridView1.CurrentCell = dataGridView1[1, i];

                    // Break out when the row is found.
                    break;
                }

            }            
        }

本应用程序允许您查询 Employees 以确定员工技能,或者查询 Skills 以确定哪些员工具有系统中所引用的技能。下面的方法从 TextBox 控件中接受用户输入,并搜索具有用户输入所描述技能的员工。

当用户将由空格分隔的多个关键字键入 TextBox 控件时,将启动对 Skills 执行的此项查询。单击 SkillSearch 按钮时,将从搜索 TextBox 控件的输入文本创建关键字列表。

通过使用 foreach 循环获取列表中的每个关键字。根据每个关键字创建 ObjectParameterObjectParameter param = new ObjectParameter("p", "%" + keyword + "%")。参数的每个新关键字值将用在一个新查询中,用于搜索系统中所有 Skills 实体的 SkillNameBriefDescription 属性:skillsQuery = DB.Skills.Where("it.BriefDescription Like @p OR it.SkillName Like @p", param)

查询结果是技能名称或描述中包含其中一个或多个关键字的 Skills 实体。这些结果将被读入 SkillsDataGridView。然后可以单击 DataGridView 中显示的行,以获取有关技能和员工推荐人的信息。单击 SkillsDataGridView 中的行将使处理程序执行相关操作(如前面的说明),并且 UI 中的 Rich Text Box 控件将显示 SkillInfo URL、员工 AliasReferences

        private void buttonSkillSearch_Click(object sender, EventArgs e)
        {
            dataGridViewSkills.DataSource = null;
            dataGridViewSkills.Columns.Clear();
            
            try
            {
                dataGridViewSkills.Columns.Add("idSkill", 
                    "Skill Id");
                dataGridViewSkills.Columns.Add("nameSkill", 
                    "Skill");
                dataGridViewSkills.Columns.Add("descSkill", 
                    "Description");
                dataGridViewSkills.Columns[2].Width = 300;

                // Make a list of keywords to search for in 
                // Skill entity name and description.
                List<string> keywords = new List<string>();
                int i = 0;
                int j = 0;

                while (i < textBoxKeywords.Text.Length)
                {
                    j = textBoxKeywords.Text.IndexOf(" ", i);
                    if (-1 == j) j = textBoxKeywords.Text.Length;
                    keywords.Add(
                        textBoxKeywords.Text.Substring(i, j - i));

                    i = ++j;
                }

                foreach (string keyword in keywords)
                {
                    // Create ObjectParameter from each keyword 
                    // and search properties of Skills.
                    ObjectParameter param = new ObjectParameter(
                        "p", "%" + keyword + "%");

                    ObjectQuery<Skills> skillsQuery = 
                        DB.Skills.Where(
                        "it.BriefDescription Like @p " +
                        "OR it.SkillName Like @p", param);

                    foreach (Skills skill in skillsQuery)
                    {
                        // Create an array of Skill property 
                        // strings for display.
                        string[] columnValues = 
                            new string[3] {skill.SkillId.ToString(),
                                skill.SkillName, 
                                skill.BriefDescription};

                        foreach (DataGridViewRow row in 
                            dataGridViewSkills.Rows)
                        {
                            // break if duplicate of 
                            // Skill already found.
                            if (row.Cells[0].Value != null)
                            {
                                if (row.Cells[0].Value.ToString()
                                    == columnValues[0])
                                {
                                    break;
                                }
                            }
                        }
                        dataGridViewSkills.Rows.Add(columnValues);
                    }
                }

                // Hide EmployeeId in datagrid, 
                // but keep for reference.
                dataGridViewSkills.Columns[0].Visible = false;

                richTextBox1.Clear();
            
            }

            catch(Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.ToString(),
                    "Error Message");
            }

        }

下面的处理程序允许用户通过在 TextBox 中按 <Enter> 而不是单击 Search 按钮来开始关键字搜索。

        private void textBoxKeywords_PreviewKeyDown(object sender, 
            PreviewKeyDownEventArgs e)
        {
            // Provide Enter activation in Search text box.
            if(e.KeyCode.Equals(Keys.Return))
                buttonSkillSearch_Click(this, System.EventArgs.Empty );
        }

当用户提交将用于创建新 Employees 实体的信息时,下面的处理程序将执行相应操作。该方法首先验证输入信息,在本例中,仅验证 textBoxAliastextBoxLastName 输入中是否都包含文本。创建新 Employees 实体后,该方法在将新实体添加到系统前将进行检查,以确定该实体是否是存储中已有的重复 Employee 实体。

创建新 Employees 实体要求 EmployeeId 属性具有新 Guid。将根据由用户提供文本的文本框中的内容为新实体的 LastNameFirstNameAliasEmail 属性赋值。

将实体添加到系统需要使用两个方法调用。第一个方法调用将新对象添加到对象上下文:DB.AddToEmployees(newEmployee)。直到第二个方法调用时才会将新实体保存到数据库:DB.SaveChanges()

        private void buttonSubmitEmployee_Click(object sender, 
            EventArgs e)
        {
            try
            {
                if ("".Equals(textBoxLastName.Text) || 
                    "".Equals(textBoxEmployeeAlias.Text))
                {
                    MessageBox.Show("Incomplete Information");
                    return;
                }

                // Create new Employee and add to storage.
                Employees newEmployee = new Employees();
                newEmployee.EmployeeId = Guid.NewGuid();
                newEmployee.LastName = textBoxLastName.Text;
                newEmployee.Alias = textBoxEmployeeAlias.Text;
                newEmployee.FirstName = textBoxFirstName.Text;
                newEmployee.Email = textBoxEmployeeEmail.Text;
                DB.AddToEmployees(newEmployee);

                // Check for duplicate.
                ObjectQuery<Employees> dbEmplQuery = 
                    DB.Employees.Where("it.Alias = @p", 
                    new ObjectParameter("p", newEmployee.Alias));

                if (!dbEmplQuery.Any())
                    DB.SaveChanges();

                //Refresh the Employees datagrid.
                EmployeesLabel_DoubleClick(this, null);

                textBoxFirstName.Clear();
                textBoxLastName.Clear();
                textBoxEmployeeAlias.Clear();
                textBoxEmployeeEmail.Clear();

            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.ToString(), 
                    "Error Message");
            }
        }

当用户提交将用于创建新 Skills 实体的信息时,下面的处理程序将执行相应操作。

        private void buttonSubmitSkill_Click(object sender, EventArgs e)
        {
            try
            {
                if ("".Equals(textBoxSkillShortName.Text) || 
                    "".Equals(textBoxSkillBriefDescription.Text) || 
                    "".Equals(textBoxSkillEmployeeId.Text))
                {
                    MessageBox.Show("Incomplete Information");
                    return;
                }

                // Create new Skills entity.
                Skills newSkills = new Skills();
                newSkills.SkillId = Guid.NewGuid();
                newSkills.SkillName = textBoxSkillShortName.Text; 
                newSkills.BriefDescription = 
                    textBoxSkillBriefDescription.Text;

                DB.AddToSkills(newSkills);

                // Make a Guid of EmployeeId of Employee who 
                // has this Skill and use it in query.
                Guid empId = 
                    new Guid(textBoxSkillEmployeeId.Text);
                ObjectParameter param = 
                    new ObjectParameter("p", empId);
                Employees employee = DB.Employees.Where(
                    "it.EmployeeId = @p", param).First();

                // Add the Skill to the Skill_Employee association.
                employee.Skills.Add(newSkills);

                DB.SaveChanges();
                textBoxSkillShortName.Clear();
                textBoxSkillBriefDescription.Clear();
                textBoxSkillEmployeeId.Clear();
                textBoxSkillEmployeeAlias.Clear();

            }

            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.ToString(),
                    "Error Message");
            }
        }

当用户提交将用于创建新 SkillInfo 实体的信息时,下面的处理程序将执行相应操作。

        private void buttonSubmitSkillInfo_Click(object sender, 
            EventArgs e)
        {
            try
            {
                if ("".Equals(textBoxSkillInfoSkillId.Text) || 
                    "".Equals(textBoxUrlUncSkillInfo.Text))
                {
                    MessageBox.Show("Incomplete Information");
                    return;
                }

                // Create new SkillInfo entity.
                SkillInfo newSkillInfo = new SkillInfo();
                newSkillInfo.SkillInfoId = Guid.NewGuid();
                newSkillInfo.URL = textBoxUrlUncSkillInfo.Text;

                // Create query and find Skill to 
                // associate with SkillInfo.
                Guid empId = 
                    new Guid(textBoxSkillInfoSkillId.Text);
                ObjectParameter param = 
                    new ObjectParameter("p", empId);
                Skills skill = 
                    DB.Skills.Where("it.SkillId = @p",
                    param).First();

                // Add SkillInfo to SkillInfo_Skill association.
                skill.SkillInfo.Add(newSkillInfo);
                DB.AddToSkillInfo(newSkillInfo);
                DB.SaveChanges();

                textBoxSkillInfoSkillId.Clear();
                textBoxUrlUncSkillInfo.Clear();
            }

            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.ToString(), 
                    "Error Message");
            }

        }

当用户提交将用于创建新 References 实体的信息时,下面的处理程序将执行相应操作。

        private void buttonSubmitReference_Click(
            object sender, EventArgs e)
        {
            try
            {
                if ("".Equals(textBoxRefEmployeeId.Text) || 
                    "".Equals(textBoxRefEmployeeAlias.Text))
                {
                    MessageBox.Show("Incomplete Information");
                    return;
                }

                // Create new Reference and add to 
                // Reference_Employee association.
                References reference = new References();
                reference.ReferenceId = Guid.NewGuid();
                reference.LastName = textBoxRefLastName.Text; 
                reference.Email = textBoxRefEmail.Text;
                reference.Alias = 
                    textBoxRefEmail.Text.Remove(
                    textBoxRefEmail.Text.IndexOf('@'));
                reference.FirstName = textBoxRefFirstName.Text;
                reference.Position = textBoxRefPosition.Text;

                Guid empId = new Guid(
                    dataGridView1.CurrentRow.Cells[0].
                    Value.ToString());
                ObjectParameter param = new ObjectParameter(
                    "p", empId);
                Employees employee = DB.Employees.Where(
                    "it.EmployeeId = @p", param).First();

                DB.AddToReferences(reference);
                employee.References.Add(reference);

                DB.SaveChanges();

                textBoxRefFirstName.Clear();
                textBoxRefLastName.Clear();
                textBoxRefEmail.Clear();
                textBoxRefPosition.Clear();

            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.ToString(), 
                    "Error Message");
            }

        }

下面的事件处理程序启动 Internet 浏览器以显示由上一方法写入 Rich Text BoxSkillInfo URL。

        private void richTextBox1_LinkClicked(object sender,
                                             LinkClickedEventArgs e)
        {  
            // Display the SkillInfo URL in Web browser.
            System.Diagnostics.Process.Start(e.LinkText);
        }

当用户双击 EmployeesDataGridView 上的标签时,下面的处理程序将执行相应操作。该处理程序用于在向系统添加新 Employees 实体后刷新 EmployeesDataGridView。在 buttonSubmitEmployee_Click 处理程序结尾处附近也会调用该处理程序。

        private void EmployeesLabel_DoubleClick(object sender, 
            EventArgs e)
        {
            try
            {
                DB.Dispose();   //Dispose to refresh the data.
                DB = new HRSkills();
                bindingSource1.DataSource = DB.Employees;
                dataGridView1.DataSource = bindingSource1;
                dataGridView1.Columns[0].Visible = false;

            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.ToString(), 
                    "Error Message");
            }
        }

另请参见

概念

人力资源技能 WinApp(EDM 示例应用程序)
HR 技能存储元数据(EDM 示例应用程序)
HR 技能映射规范(EDM 示例应用程序)