Поделиться через


Код приложения HR Skills (образец приложения модели EDM)

Код, который используется в приложении HRSkills, демонстрирует несколько возможностей модели Entity Data Model (EDM). Схемы, описанные в предыдущих разделах HRSkills, являются основой для сущностей и ассоциаций, которые используются в коде в этом разделе. Сведения о сущностях и ассоциациях в этом примере см. в разделе Приложение Human Resources Skills для Windows (образец приложения модели EDM). Дополнительные сведения о метаданных хранилища см. в разделе Метаданные хранилища HR Skills (образец приложения модели EDM). Сведения о спецификации сопоставления см. в разделе Спецификация сопоставления HR Skills (образец приложения модели EDM).

Типы, сконструированные в схемах и сопоставленные с хранилищем, создаются в виде программируемой модели объектов. Данные в такой модели можно программировать с использованием синтаксиса среды CLR без SQL-запросов, внедренных в код в виде строк.

Приложение демонстрирует привязку данных к сущностям, параметризованные запросы и использование свойств навигации для ассоциаций. Ассоциации соединяют сущности Employees с сущностями References, сущности Employees с сущностями Skills, а сущности Skills с сущностями SkillInfo, содержащими сведения о навыках.

Файл конфигурации и строка соединения

Для использования модели объектов необходимо соединение с базой данных, в которой хранятся данные приложения. Необходимо также соединение сущности с объектами времени выполнения, предоставляемыми созданной из схем библиотекой. Сведения о построении модели объектов из схем см. в разделе «Как Как использовать программу EdmGen.exe для формирования модели EDM (Entity Framework).

Файл exe.config содержит строку соединения, которая используется для соединения с базой данных SQL Server и установления соединения сущности. При наличии соединения сущности можно из кода получить доступ к сущностям и ассоциациям модели объектов.

Текст строки соединения должен быть добавлен в файл exe.config разработчиком. В этом приложении определяется класс HRSkills. Назначение providerName="System.Data.EntityClient" задает соединение с сущностью, в котором используется схема сопоставления, определенная в разделе Спецификация сопоставления HR Skills (образец приложения модели 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, которая служит пользовательским интерфейсом в этом образце приложения. Директивы препроцессора 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 представляет собой экземпляр запроса ObjectQuery<T> в контексте ObjectContext, который возвращает все сущности сотрудников.

Элемент управления DataGridView используется для отображения результатов запроса Employees. К элементу управления DataGridView добавляются столбцы. BindingSource инициализируется значением Query<T> элемента Employee, а свойство DataSource элемента управления привязывает данные. Использование параметра true приводит к обновлению базы данных после того, как в элементе управления выполнены все изменения DataGrid.

        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());
            }
        }

После отображения элементов Employees в элементе управления DataGridView пользователь приложения может щелкнуть любую строку, чтобы получить навыки Skills, связанные с сущностью Employee в этой строке. Новый запрос находит идентификатор EmployeeId сущности Employee, данные которой отображаются в DataGridView. Идентификатор EmployeeId скрыт при отображении в DataGridView, но хранится для ссылки. Он используется в параметризованном запросе ObjectQuery<T> к Employees. Параметр создается и инициализируется в строке ObjectParameter param = new ObjectParameter("p", empId). В запросе используется параметр ObjectParameterDB.Employees.Where("it.EmployeeId = @p", param).First().

После определения и возвращения запросом класса employee для поиска всех навыков Skills, связанных с этим сотрудником, используется ассоциация Skill_Employee. Одна строка кода использует свойство навигации сотрудника для загрузки из базы данных всех навыков Skills, связанных с этим сотрудником: Skill_Employee.GetSkillsEntities(employee).Load(). Цикл foreach в том же свойстве навигации считывает навыки 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());
            }

        }

Следующий метод отображает сведения о навыках Skills, которые содержатся в сущностях SkillInfo в элементе управления RichTextBox пользовательского интерфейса. Сущности SkillInfo, связанные с сущностями Skills, запрашиваются и выводятся с помощью того же процесса, что и в предыдущем методе. Запрос принимает идентификатор SkillId в качестве параметра и находит конкретную сущность Skills. Свойство навигации сущности Skills используется для поиска всех сущностей SkillInfo, связанных с этим навыком.

Свойства сущностей SkillInfo содержат URL-адреса, по которым находятся дополнительные сведения о связанных навыках Skills. URL-адрес в каждой сущности SkillInfo отображается в элементе управления rich text box.

Доступ к сущности employee, связанной с сущностью Skills, которая используется в этом методе, можно также получить из сущности Skills через ассоциацию Skill_Employee. Ссылка на сотрудника загружается из базы данных с помощью одной строки кода: Skill_Employee.GetEmployeesRef(skill).Load(). Сотрудник присваивается переменной во второй строке: Employees employee = Skill_Employee.GetEmployees(skill). Псевдоним сотрудника отображается в элементе управления rich text box.

Ссылки, связанные с сотрудником, передаются из ассоциации Reference_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 и выполняется поиск сотрудников, навыки которых описаны во входных данных.

Запрос к сущностям Skills инициируется, когда пользователь вводит ключевые слова, разделенные пробелом, в элемент управления TextBox. Когда нажимается кнопка SkillSearch, из ввода пользователя в текстовом поле поиска TextBox создается список ключевых слов.

Каждое ключевое слово получается с помощью цикла foreach. Из каждого ключевого слова создается параметр ObjectParameter: ObjectParameter param = new ObjectParameter("p", "%" + keyword + "%"). Каждое новое значение ключевого слова для этого параметра используется в новом запросе, который выполняет поиск в свойствах SkillName и BriefDescription всех сущностей Skills в системе: skillsQuery = DB.Skills.Where("it.BriefDescription Like @p OR it.SkillName Like @p", param).

Результатами запроса являются сущности Skills, содержащие имена навыков или описания, имеющие одно или несколько ключевых слов. Эти результаты считываются в SkillsDataGridView. После этого можно щелкать строки в элементе управления DataGridView, чтобы выводить сведения о навыках и ссылки на сотрудников. Щелчок строки в элементе управления DataGridViewSkills вызывает обработчик, как описано выше, и в элементе управления Rich Text Box пользовательского интерфейса отображаются URL-адреса SkillInfo, псевдоним Alias и ссылки References сотрудника.

        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 и тем самым начать поиск ключевых слов, вместо того, чтобы нажимать кнопку 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. Метод вначале проверяет входные данные. В этом случае он лишь проверяет, что ввод в текстовых полях textBoxLastName и textBoxAlias содержит текст. После создания новой сущности Employees метод проверяет, не является ли она дубликатом сущности Employee, уже имеющейся в хранилище, прежде чем добавить новую сущность в систему.

При создании новой сущности Employees требуется новый идентификатор Guid для свойства EmployeeId. Свойства LastName, FirstName, Alias и Email новой сущности назначаются из содержимого текстовых полей, в которые пользователь вводит текст.

Чтобы добавить сущность в систему, требуется вызвать два метода. Первый добавляет новый объект в контекст объектов: 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");
            }

        }

Следующий обработчик запускает Интернет-обозреватель, чтобы отобразить URL-адрес SkillInfo, записанный в элемент управления Rich Text Box предыдущим методом.

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

Следующий обработчик вызывается, когда пользователь дважды щелкает метку в EmployeesDataGridView. Он предназначен для обновления элемента управления EmployeesDataGridView, когда в систему добавляются новые сущности Employees. Он также вызывается при завершении обработчика 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");
            }
        }

См. также

Основные понятия

Приложение Human Resources Skills для Windows (образец приложения модели EDM)
Метаданные хранилища HR Skills (образец приложения модели EDM)
Спецификация сопоставления HR Skills (образец приложения модели EDM)