Код приложения 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)