## 목차
1) Layered Architecture
2) Model
3) Repository
4) Service
5) Form
6) 회고
#1 Layered Architecture
본 프로젝트는 회사의 과제로 진행하였습니다.
프로젝트 개발을 하기 앞서 요구사항정의서와 UI 기능 정의서를 작성한 후 아래와 같이 아키텍쳐를 구상하였습니다.
DB는 MSsql을 사용하였으며 Repository 저장소에 데이터를 controll 하는 query문을 작성하여 담아두었습니다.
(DevTool : DBever, Visual Studio 2022)
그리고 비즈니스로직을 처리하는 Service에서 Repository에 담아둔 query문을 호출할 수 있는 매개채 역할을 수행합니다.
마지막으로 Form에서는 사용자가 Controll 할 수 있는 UI 부분과 해당 기능에 따른 이벤트 핸들러를 동작하도록 구상하였습니다.
#2 Model
아래와 같이 각 칼럼에 대한 스키마를 지정해주었습니다.
Name 속성은 ToString() 메서드를 재정의하여 객체의 문자열 표현을 반환하기 위해서입니다.
그 이유는 후에 보면 알겠지만, ListBox에 Name 객체만 목록에 뿌려주기 위함입니다.
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Employee
{
public class Pj_Employee
{
[Key]
[Column("Id")]
public string Id { get; set; }
[Key]
[Column("Name")]
public string Name { get; set; }
public override string ToString()
{
return Name;
}
[Key]
[Column("Position")]
public string Position { get; set; }
[Key]
[Column("Birthdate")]
public DateTime Birthdate { get; set; }
[Key]
[Column("CellPhone")]
public string CellPhone { get; set; }
[Key]
[Column("Email")]
public string Email { get; set; }
[Key]
[Column("UiSeq")]
public string UiSeq { get; set; }
}
}
#3 Repository
해당 코드는 일반적인 CRUD 레포지토리 패턴을 구현하기 위해 제네릭 인터페이스를 정의하였습니다.
여러 유형의 엔티티를 다룰 수 있는 일반적인 데이터 액세스 계층을 만들 수 있으며, 이를 통해 코드 재사용성과 유지보수성이 향상됩니다.
IGenericRepository.cs
using System.Collections.Generic;
namespace Employee
{
interface IGenericRepository<T>
{
T GetById(int id);
IEnumerable<T> GetAll();
int Add(T entity);
int Update(T entity);
int Delete(int entity);
}
}
또한, Dapper를 이용한 데이터 액세스를 하였습니다.
Dapper는 빠르고 가벼운 마이크로 ORM(Object-Relational Mapping) 라이브러리로, SQL 쿼리와 개체 간의 매핑을 담당합니다. 이 코드에서는 Dapper를 사용하여 데이터베이스 액세스를 수행하므로 간결하고 효율적인 데이터 액세스를 구현할 수 있습니다.
GenericRepository.cs
using Dapper;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Configuration;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;
namespace Employee
{
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
public SqlConnection _sqlConnection;
public SqlTransaction _sqlTransaction;
public object _syncDB = new object();
private bool _isOpen = false;
readonly string connectionString = ConfigurationManager.ConnectionStrings["EMPLOYEE_MS"].ConnectionString;
public bool IsOpen
{
get
{
if (_sqlConnection == null)
{
_isOpen = false;
return _isOpen;
}
if (_sqlConnection.State == System.Data.ConnectionState.Open)
{
_isOpen = true;
}
else
{
_isOpen = false;
}
return _isOpen;
}
}
public void OpenDB()
{
try
{
lock (_syncDB)
{
if (_sqlConnection == null)
{
_sqlConnection = new SqlConnection(connectionString);
}
if (_sqlConnection.State == System.Data.ConnectionState.Closed)
{
_sqlConnection.ConnectionString = connectionString;
_sqlConnection.Open();
}
}
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
public void CloseDB()
{
try
{
lock (_syncDB)
{
if (_sqlConnection == null)
return;
_sqlConnection.Close();
_sqlConnection.Dispose();
_sqlConnection = null;
}
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
#region Transaction
public void BeginTransaction()
{
lock (_syncDB)
_sqlTransaction = _sqlConnection.BeginTransaction();
}
public void CommitTransaction()
{
lock ( _syncDB)
{
if (_sqlTransaction != null)
{
_sqlTransaction.Commit();
_sqlTransaction = null;
}
}
}
public void RollbackTransaction()
{
lock (_syncDB)
{
if (_sqlTransaction != null)
{
_sqlTransaction.Rollback();
_sqlTransaction = null;
}
}
}
#endregion
public int Add(T entity)
{
lock(_syncDB)
{
int rowsEffected = 0;
try
{
string tableName = GetTableName();
string columns = GetColumns(excludeKey: true);
string properties = GetPropertyNames(excludeKey: true);
string query = $"INSERT INTO {tableName} ({columns}) VALUES ({properties})";
rowsEffected = _sqlConnection.Execute(query, entity);
return rowsEffected;
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
}
public int Delete(int entity)
{
lock (_syncDB)
{
int rowsEffected = 0;
try
{
string tableName = GetTableName();
string keyColumn = GetKeyColumnName();
string keyProperty = GetKeyPropertyName();
string query = $"DELETE FROM {tableName} WHERE {keyColumn} = @{keyProperty}";
rowsEffected = _sqlConnection.Execute(query, entity);
return rowsEffected;
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
}
public IEnumerable<T> GetAll()
{
lock (_syncDB)
{
IEnumerable<T> result = null;
try
{
if (IsOpen == false)
OpenDB();
string tableName = GetTableName();
string query = $"SELECT * FROM {tableName}";
result = _sqlConnection.Query<T>(query);
return result;
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
finally
{
CloseDB();
}
}
}
public T GetById(int Id)
{
lock (_syncDB)
{
IEnumerable<T> result = null;
try
{
if (IsOpen == false)
OpenDB();
string tableName = GetTableName();
string keyColumn = GetKeyColumnName();
string query = $"SELECT * FROM {tableName} WHERE {keyColumn} = '{Id}'";
result = _sqlConnection.Query<T>(query);
return result.FirstOrDefault();
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
finally
{
CloseDB();
}
}
}
public int Update(T entity)
{
lock (_syncDB)
{
int rowsEffected = 0;
try
{
string tableName = GetTableName();
string keyColumn = GetKeyColumnName();
string keyProperty = GetKeyPropertyName();
StringBuilder query = new StringBuilder();
query.Append($"UPDATE {tableName} SET ");
foreach (var property in GetProperties(true))
{
var columnAttr = property.GetCustomAttribute<ColumnAttribute>();
string propertyName = property.Name;
string columnName = columnAttr.Name;
query.Append($"{columnName} = @{propertyName},");
}
query.Remove(query.Length - 1, 1);
query.Append($" WHERE {keyColumn} = @{keyProperty}");
rowsEffected = _sqlConnection.Execute(query.ToString(), entity);
return rowsEffected;
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
}
#region Methods
private string GetTableName()
{
string tableName = "";
var type = typeof(T);
var tableAttr = type.GetCustomAttribute<TableAttribute>();
if (tableAttr != null)
{
tableName = tableAttr.Name;
return tableName;
}
return type.Name;
}
public static string GetKeyColumnName()
{
PropertyInfo[] properties = typeof(T).GetProperties();
foreach (PropertyInfo property in properties)
{
object[] keyAttributes = property.GetCustomAttributes(typeof(KeyAttribute), true);
if (keyAttributes != null && keyAttributes.Length > 0)
{
object[] columnAttributes = property.GetCustomAttributes(typeof(ColumnAttribute), true);
if (columnAttributes != null && columnAttributes.Length > 0)
{
ColumnAttribute columnAttribute = (ColumnAttribute)columnAttributes[0];
return columnAttribute.Name;
}
else
{
return property.Name;
}
}
}
return null;
}
private string GetColumns(bool excludeKey = false)
{
var type = typeof(T);
var columns = string.Join(", ", type.GetProperties()
.Where(p => !excludeKey || !p.IsDefined(typeof(KeyAttribute)))
.Select(p =>
{
var columnAttr = p.GetCustomAttribute<ColumnAttribute>();
return columnAttr != null ? columnAttr.Name : p.Name;
}));
return columns;
}
protected string GetPropertyNames(bool excludeKey = false)
{
var properties = typeof(T).GetProperties()
.Where(p => !excludeKey || p.GetCustomAttribute<KeyAttribute>() == null);
var values = string.Join(", ", properties.Select(p =>
{
return $"@{p.Name}";
}));
return values;
}
protected IEnumerable<PropertyInfo> GetProperties(bool excludeKey = false)
{
var properties = typeof(T).GetProperties()
.Where(p => !excludeKey || p.GetCustomAttribute<KeyAttribute>() == null);
return properties;
}
protected string GetKeyPropertyName()
{
var properties = typeof(T).GetProperties()
.Where(p => p.GetCustomAttribute<KeyAttribute>() != null);
if (properties.Any())
{
return properties.FirstOrDefault().Name;
}
return null;
}
#endregion
}
}
이 클래스는 주로 Pj_Employee 엔티티에 대한 데이터 액세스를 수행합니다. 제네릭 리포지토리를 통해 데이터베이스와의 상호 작용을 단순화하고 일반화합니다.
아래는 코드가 한 이유와 각 메서드의 역할에 대한 설명입니다.
상속: EmployeeRepository 클래스가 GenericRepository<Pj_Employee>를 상속하는 이유는 제네릭 리포지토리에서 제공되는 CRUD(Create, Read, Update, Delete) 기능을 Pj_Employee 엔티티에 대해 재사용하기 위함입니다.
1. AddEmployee 메서드: Pj_Employee를 추가하는 데 사용됩니다. Pj_Employee 엔티티의 데이터를 데이터베이스에 삽입하는 쿼리를 실행합니다.
2. UpdateEmployee 메서드: Pj_Employee를 업데이트하는 데 사용됩니다. Pj_Employee 엔티티의 데이터를 수정하는 쿼리를 실행합니다.
3. DeleteEmployee 메서드: Pj_Employee를 삭제하는 데 사용됩니다. Pj_Employee 엔티티의 데이터를 삭제하는 쿼리를 실행합니다.
4. GetPjEmployeeList 메서드: 데이터베이스에서 모든 Pj_Employee 엔티티를 가져옵니다. 쿼리를 실행하여 Pj_Employee 엔티티의 목록을 반환합니다.
5. IsIdExists 메서드: 특정 ID를 가진 Pj_Employee가 데이터베이스에 존재하는지 확인합니다. 쿼리를 실행하여 해당 ID를 가진 레코드의 존재 여부를 확인하고 결과를 부울 값으로 반환합니다.
EmployeeRepository.cs
using Dapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Employee
{
public class EmployeeRepository : GenericRepository<Pj_Employee>
{
public int AddEmployee(Pj_Employee pjEmployee)
{
lock (_syncDB)
{
int rowsEffected = 0;
try
{
if (IsOpen == false)
OpenDB();
string query = @"
insert into Employee (
Id,
Name,
Position,
Birthdate,
CellPhone,
Email,
UiSeq
) values (
@Id,
@Name,
@Position,
@Birthdate,
@CellPhone,
@Email,
@UiSeq
)
";
BeginTransaction();
rowsEffected += _sqlConnection.Execute(query.ToString(), pjEmployee, transaction: _sqlTransaction);
CommitTransaction();
return rowsEffected;
}
catch (Exception ex)
{
RollbackTransaction();
throw new Exception(ex.ToString());
}
finally
{
CloseDB();
}
}
}
public int UpdateEmployee(Pj_Employee pjEmployee)
{
lock (_syncDB)
{
int rowsEffected = 0;
try
{
if (IsOpen == false)
{
OpenDB();
}
string query = @"
update Employee
set Name = @Name,
Position = @Position,
Birthdate = @Birthdate,
CellPhone = @CellPhone,
Email = @Email,
UiSeq = @UiSeq
where Id = @Id
";
BeginTransaction();
rowsEffected += _sqlConnection.Execute(query.ToString(), pjEmployee, transaction: _sqlTransaction);
CommitTransaction();
return rowsEffected;
}
catch(Exception ex)
{
RollbackTransaction();
throw new Exception(ex.ToString());
}
finally
{
CloseDB();
}
}
}
public int DeleteEmployee(Pj_Employee pjEmployee)
{
lock ( _syncDB)
{
int rowsEffected = 0;
try
{
if (IsOpen == false)
{
OpenDB();
}
string query = @"
delete
from Employee
where 1 = 1
and Id = @Id
";
BeginTransaction();
rowsEffected = _sqlConnection.Execute(query.ToString(), pjEmployee, transaction: _sqlTransaction);
CommitTransaction();
return rowsEffected;
} catch (Exception ex)
{
RollbackTransaction();
throw new Exception(ex.ToString());
}
finally
{
CloseDB();
}
}
}
public List<Pj_Employee> GetPjEmployeeList()
{
lock (_syncDB)
{
List<Pj_Employee> pjEmployeeList = new List<Pj_Employee>();
try
{
if (IsOpen == false)
{
OpenDB();
}
string query = @"
select
A.Id AS Id
,A.Name AS Name
,A.Position AS Position
,A.Birthdate AS Birthdate
,A.CellPhone AS CellPhone
,A.Email AS Email
,Convert(int, Isnull(A.UiSeq, '0')) AS UiSeq
from Employee A
where 1=1
order by UiSeq asc
";
pjEmployeeList = _sqlConnection.Query<Pj_Employee>(query).ToList();
return pjEmployeeList;
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
finally
{
CloseDB();
}
}
}
public bool IsIdExists(string id)
{
lock ( _syncDB)
{
try
{
if (IsOpen == false)
{
OpenDB();
}
string query = @"
select
count(*)
from Employee
where Id = @id
";
int count = _sqlConnection.ExecuteScalar<int>(query, new { Id = id });
return count > 0;
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
finally
{
CloseDB();
}
}
}
}
}
#4 Service
해당 클래스는 애플리케이션의 UI와 데이터 액세스 간의 중개자 역할을 합니다. 주요 기능은 사용자의 요청에 따라 데이터베이스와 상호 작용하고 그 결과를 반환하는 것입니다.
그리고 UI와 데이터 액세스 간의 결합을 줄이고 코드의 재사용성과 유지 보수성을 향상시키기 위해 사용됩니다. 이 클래스는 비즈니스 로직을 캡슐화하여 UI 컴포넌트가 직접 데이터베이스와 상호 작용하지 않고도 데이터 액세스를 수행할 수 있도록 합니다.
EmployeeService.cs
using System;
using System.Collections.Generic;
namespace Employee
{
public class EmployeeService
{
public int AddEmployee(Pj_Employee pjEmployee)
{
int isAdded = 0;
try
{
EmployeeRepository employeeRepository = new EmployeeRepository();
isAdded = employeeRepository.AddEmployee(pjEmployee);
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
return isAdded;
}
public int UpdateEmployee(Pj_Employee pjEmployee)
{
int isUpdated = 0;
try
{
EmployeeRepository employeeRepository = new EmployeeRepository();
isUpdated = employeeRepository.UpdateEmployee(pjEmployee);
}
catch(Exception ex)
{
throw new Exception (ex.ToString());
}
return isUpdated;
}
public int DeleteEmployee(Pj_Employee pjEmployee)
{
int isDeleted = 0;
try
{
EmployeeRepository employeeRepository = new EmployeeRepository();
isDeleted = employeeRepository.DeleteEmployee(pjEmployee);
}
catch (Exception ex)
{
throw new Exception (ex.ToString ());
}
return isDeleted;
}
public List<Pj_Employee> GetPjEmployeeList()
{
List<Pj_Employee> pjEmployeeList = new List<Pj_Employee>();
try
{
EmployeeRepository employeeRepository = new EmployeeRepository();
pjEmployeeList = employeeRepository.GetPjEmployeeList();
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
return pjEmployeeList;
}
public bool IsIdExists(string id)
{
EmployeeRepository employeeRepository= new EmployeeRepository();
return employeeRepository.IsIdExists(id);
}
}
}
#5 Form
해 클래스는 임직원 정보를 표시하고 편집하는 데 사용되는 Windows Forms 애플리케이션의 사용자 인터페이스를 구성합니다. 이 클래스는 Windows Forms 디자이너를 사용하여 디자인되었으며 사용자가 프로그램을 실행할 때 화면에 표시됩니다.
임직원 데이터의 표시와 편집을 위한 사용자 친화적인 화면을 제공하고, UI 이벤트에 대한 적절한 처리를 통해 데이터의 유효성을 검사하고 사용자에게 피드백을 제공합니다.
EmployeeForm.cs
using System;
using System.Data;
using System.Linq;
using System.Windows.Forms;
namespace Employee
{
public partial class EmployeeForm : Form
{
#region Variables
private Pj_Employee _selectedEmployee;
ToolTip _toolTip = new ToolTip();
private string _mode = string.Empty;
#endregion
#region Constructor
public EmployeeForm()
{
InitializeComponent();
Initialize();
LoadEmployeeList();
DisableTextBox();
}
#endregion
#region Method
private void Initialize()
{
this.CenterToScreen();
}
private void LoadEmployeeList()
{
dtpBirthdate.MinDate = new DateTime(1965, 1, 1);
dtpBirthdate.MaxDate = DateTime.Today;
EmployeeService employeeService = new EmployeeService();
var employeeList = employeeService.GetPjEmployeeList();
employeeList = employeeList.OrderBy(employee => int.Parse(employee.UiSeq)).ToList();
lbxEmployeeList.DisplayMember = "Name";
lbxEmployeeList.ValueMember = "Id";
lbxEmployeeList.DataSource = employeeList;
mtbCellPhone.Mask = "000-0000-0000";
mtbCellPhone.MaskInputRejected += new MaskInputRejectedEventHandler(mtbCellPhone_MaskInputRejected);
}
private void EnableTextBox()
{
txbId.Enabled = true;
txbName.Enabled = true;
txbPosition.Enabled = true;
dtpBirthdate.Enabled = true;
txbPosition.Enabled = true;
mtbCellPhone.Enabled = true;
txbEmail.Enabled = true;
txbUiSeq.Enabled = true;
}
private void DisableTextBox()
{
txbId.Enabled = false;
txbName.Enabled = false;
txbPosition.Enabled = false;
dtpBirthdate.Enabled = false;
txbPosition.Enabled = false;
mtbCellPhone.Enabled = false;
txbEmail.Enabled = false;
txbUiSeq.Enabled = false;
}
private bool IsDuplicatedId(string id)
{
EmployeeService employeeService = new EmployeeService();
return employeeService.IsIdExists(id);
}
#endregion
private void lbxEmployeeList_SelectedIndexChanged(object sender, EventArgs e)
{
if (lbxEmployeeList.SelectedItem != null)
{
_selectedEmployee = (Pj_Employee)lbxEmployeeList.SelectedItem;
txbId.Text = _selectedEmployee.Id;
txbName.Text = _selectedEmployee.Name;
txbPosition.Text = _selectedEmployee.Position;
dtpBirthdate.Value = _selectedEmployee.Birthdate;
mtbCellPhone.Text = _selectedEmployee.CellPhone;
txbEmail.Text = _selectedEmployee.Email;
txbUiSeq.Text = _selectedEmployee.UiSeq;
}
}
private void btnSearch_Click(object sender, EventArgs e)
{
LoadEmployeeList();
DisableTextBox();
}
private void btnCreate_Click(object sender, EventArgs e)
{
try
{
_mode = "C";
_selectedEmployee = null;
txbId.Text = string.Empty;
txbName.Text = string.Empty;
txbPosition.Text = string.Empty;
dtpBirthdate.Value = DateTime.Today;
mtbCellPhone.Text = string.Empty;
txbEmail.Text = string.Empty;
txbUiSeq.Text = string.Empty;
EnableTextBox();
MessageBox.Show("새로운 임직원 정보를 입력하세요.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnUpdate_Click(object sender, EventArgs e)
{
if (_selectedEmployee != null)
{
try
{
_mode = "U";
EnableTextBox();
MessageBox.Show("임직원 정보를 수정하세요.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
MessageBox.Show("선택된 임직원이 없습니다.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void dtpBirthdate_ValueChanged(object sender, EventArgs e)
{
DateTime dt = dtpBirthdate.Value;
}
private void btnSave_Click(object sender, EventArgs e)
{
try
{
EmployeeService employeeService = new EmployeeService();
if (string.IsNullOrEmpty(txbId.Text))
{
MessageBox.Show("사번을 입력해주세요.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
txbId.Focus();
return;
}
if (string.IsNullOrEmpty(txbName.Text))
{
MessageBox.Show("성명을 입력해주세요.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
txbName.Focus();
return;
}
if (_mode == "C")
{
Pj_Employee pjEmployee = new Pj_Employee
{
Id = txbId.Text,
Name = txbName.Text,
Position = txbPosition.Text,
Birthdate = dtpBirthdate.Value,
CellPhone = mtbCellPhone.Text,
Email = txbEmail.Text,
UiSeq = txbUiSeq.Text
};
if (!IsDuplicatedId(txbId.Text))
{
int rowsEffected = employeeService.AddEmployee(pjEmployee);
MessageBox.Show("임직원 정보가 저장되었습니다.");
DisableTextBox();
}
else
{
MessageBox.Show("중복된 사번이 있습니다. 사번을 다시 입력하세요.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
EnableTextBox();
return;
}
}
else if (_mode == "U")
{
if (_selectedEmployee != null)
{
_selectedEmployee.Name = txbName.Text;
_selectedEmployee.Position = txbPosition.Text;
_selectedEmployee.Birthdate = dtpBirthdate.Value;
_selectedEmployee.CellPhone = mtbCellPhone.Text;
_selectedEmployee.Email = txbEmail.Text;
_selectedEmployee.UiSeq = txbUiSeq.Text;
if (txbId.Text != _selectedEmployee.Id && IsDuplicatedId(txbId.Text))
{
MessageBox.Show("중복된 사번이 있습니다. 사번을 다시 입력하세요.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
EnableTextBox();
return;
}
_selectedEmployee.Id = txbId.Text;
employeeService.UpdateEmployee(_selectedEmployee);
MessageBox.Show("임직원 정보가 수정되었습니다.");
DisableTextBox();
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
LoadEmployeeList();
}
private void btnDelete_Click(object sender, EventArgs e)
{
try
{
EmployeeService employeeService = new EmployeeService();
Pj_Employee pjEmployee = new Pj_Employee();
employeeService.DeleteEmployee(_selectedEmployee);
if (MessageBox.Show("정말로 삭제하시겠습니까?", "삭제 확인", MessageBoxButtons.YesNo) == DialogResult.Yes);
LoadEmployeeList();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void mtbCellPhone_MaskInputRejected(object sender, MaskInputRejectedEventArgs e)
{
if (mtbCellPhone.MaskFull)
{
_toolTip.ToolTipTitle = "Input Rejected : Too Much Data";
_toolTip.Show("You cannot enter any more data inito the date field.Delete some characters in order to insert more data.", mtbCellPhone, 0, -20, 4000);
}
else if (e.Position == mtbCellPhone.Mask.Length)
{
_toolTip.ToolTipTitle = "Input Rejected : Use Numeric";
_toolTip.Show("You can only add numeric characters (0-9) into this cellphone field", mtbCellPhone, 0, -20, 4000);
}
}
private void txbUiSeq_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsDigit(e.KeyChar) && e.KeyChar != (char)Keys.Back)
{
MessageBox.Show("숫자를 입력해주세요.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Handled = true;
}
}
private void txbId_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsDigit(e.KeyChar) && e.KeyChar != (char)Keys.Back)
{
MessageBox.Show("숫자를 입력해주세요", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Handled = true;
}
else if (txbId.Text.Length == 0 && e.KeyChar == '0')
{
MessageBox.Show("사번은 0으로 시작할 수 없습니다.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Handled = true;
}
}
}
}
#6 회고
C# 언어에 대해서는 회사에 입사하고 공부를 시작하여 많이 부족한점이 많다고 느끼고 있었습니다.
그래도 회사에서 공부할 수 있는 기회를 주었고 그 시간을 바탕으로 과제를 내주어 감사하다고 생각합니다.
프로그램이 아직 완성되었다고는 할 수 없지만, 많은 고민을 하며 깨닫게 된 부분도 많았고, 검색을 많이 하면서 알게된 점도 많았습니다.
그래서 회고 목차에서는 아래와 같이 제가 개발을 하면서 핵심적인 내용이고, 새로 알게된 부분에 대해서 짚고 넘어가보도록 하겠습니다.
1. Sql Connect :
먼저 MSsql은 사용해 본적이 없어서 접근하기가 어려웠습니다. 그리고 java를 배울 때 mybatis처럼 query를 mapping해서 데이터를 저장하고 추출하고 싶었습니다. 그런데 검색에는 대부분 sqlConnection 연결하는 부분까지만 나와 어려움이 있었습니다.
그래서 회사분들께 많은 배움을 받으면서 위와 같이 sqlConnection 및 mapping 하는 방법을 배우게 되었습니다. App.config 파일에서 새로 연결할 DB에 대한 정보를 저장할 때 GenericRepository.cs 부분에 Connectstring 부분에 연결하고자 하는 DB의 이름을 임의로 지정을 해주어 가독성이 향상되고 재사용성이 높아졌습니다.
그리고 해당 프로젝트는 워낙 작은 프로젝트라 데이터라든지 스레드의 충돌이 일어날 일은 없지만, 배우면서 개발하는거기에 Transaction 처리에 대한 부분도 구현해보았습니다.
BeginTransaction()을 통해 다중 스레드 환경에서 여러 트랜잭션이 발생하는 것을 lock()을 통해 방지하고, CommitTransaction()메서드를 통해 현재 진행 중인 트랜잭션이 성공적으로 완료되면 _sqlTransaction을 null로 설정하여 트랜잭션이 더 이상 사용되지 않음을 나타내며 lock ()를 사용하여 동시성 문제를 방지합니다.
마지막으로 RollbackTransaction() 메서드를 통해 트랜잭션 실행 중 예외가 발생한 경우 롤백하여 이전 상태로 복원할 수 있습니다.
2. coming..
'ZAION > C#' 카테고리의 다른 글
[이것이 C#이다]Ch.04 데이터를 가공하는 연산자 (0) | 2024.01.29 |
---|---|
[이것이 C#이다]Ch.03 데이터 보관하기 - 2 (2) | 2024.01.25 |
[이것이 C#이다]Ch.08 인터페이스와 추상 클래스 (0) | 2024.01.22 |
[이것이 C#이다]Ch.03 데이터 보관하기 - 1 (0) | 2024.01.12 |
[C#][인프런]C# 프로그래밍 기초(4~5강) (0) | 2024.01.08 |