This is my first experience of designing classes with inheritance. I would like to get feedback on the interaction between the presented classes. It is unlikely that this code will seem complicated. But if you need a description of it, please specify in the comments.
There are several classes that describe the model
.
ToolStripUI
Сontains properties for configuring the UI.SearchArgs
Inherited fromEventArgs
. Required to pass data to the search method.PagedModelBase<T>
It is necessary to implement the output of data on the page.SearchModel<T>
Extension forPagedModelBase
in the search part.TableModel<T>
Extension forPagedModelBase
in normal mode. Forces theFind()
andDelete()
methods to be implemented.
They are used to create two models: Contract
and Worker
. They inherit TableModel
. And each has a inner class SearchModel
that inherits SearchModel<T>
.
A change in the PagedModelBase<T>
properties causes a change in ToolStripUI
. Thus, ToolStripUI
always contains relevant data for UI.
The following code shows how the Contract model
methods are used.
private void OnButtonToolStripClick(object sender, TSButton btn)
{
switch (form.CurrentTable)
{
case TableName.CONTRACT:
if (model.GetContractModel.GetSearch.IsSearch)
{
SearchTableContract(btn);
}
else
{
TableContract(btn);
}
break;
case TableName.WORKER:
if (model.GetWorkerModel.GetSearch.IsSearch)
{
SearchTableWorker(btn);
}
else
{
TableWorker(btn);
}
break;
}
}
private void SearchTableContract(TSButton btn)
{
List<ContractTableRow> list;
ToolStripUI ui;
switch (btn)
{
case TSButton.SEARCH:
list = model.GetContractModel.Find(form.GetSearchItem());
break;
case TSButton.RESET:
list = model.GetContractModel.ReloadPage();
break;
case TSButton.FIRST:
list = model.GetContractModel.GetSearch.FirstPage();
break;
case TSButton.LAST:
list = model.GetContractModel.GetSearch.LastPage();
break;
case TSButton.NEXT:
list = model.GetContractModel.GetSearch.NextPage();
break;
case TSButton.PREVIOUS:
list = model.GetContractModel.GetSearch.PreviousPage();
break;
default:
list = null;
break;
}
if (btn == TSButton.RESET)
{
ui = model.GetContractModel;
}
else
{
ui = model.GetContractModel.GetSearch;
}
if (list.Count > 0)
{
contractUC.FillPage(list);
form.SetToolStipUI(ui);
}
else
{
ShowMessage.Information("Не найдено");
}
}
private void TableContract(TSButton btn)
{
List<ContractTableRow> list;
ToolStripUI ui;
switch (btn)
{
case TSButton.SEARCH:
list = model.GetContractModel.Find(form.GetSearchItem());
break;
case TSButton.RESET:
list = model.GetContractModel.ReloadPage();
break;
case TSButton.FIRST:
list = model.GetContractModel.FirstPage();
break;
case TSButton.LAST:
list = model.GetContractModel.LastPage();
break;
case TSButton.NEXT:
list = model.GetContractModel.NextPage();
break;
case TSButton.PREVIOUS:
list = model.GetContractModel.PreviousPage();
break;
default:
list = null;
break;
}
if (btn == TSButton.SEARCH)
{
ui = model.GetContractModel.GetSearch;
}
else
{
ui = model.GetContractModel;
}
contractUC.FillPage(list);
form.SetToolStipUI(ui);
}
And the full code of all described classes. Perhaps more convenient to watch via GitHub.
public ContractModel GetContractModel { get; private set; }
public WorkerModel GetWorkerModel { get; private set; }
public MainModel()
{
GetContractModel = new ContractModel();
GetWorkerModel = new WorkerModel();
}
public class ContractModel : TableModel<ContractTableRow>
{
internal SearchModel GetSearch { get; set; }
public ContractModel()
{
Size = Properties.Settings.Default.TableContractSize;
GetSearch = new SearchModel();
}
internal override bool Delete(int id)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
bool result = true;
try
{
using (ModelContext model = new ModelContext())
{
Contracts c = model.Contracts.Single(x => x.Id == id);
model.Remove(c);
model.SaveChanges();
}
Count--;
}
catch (Exception ex)
{
result = false;
ShowMessage.Error(string.Format(
"Во время удаления договора из базы данных произошла ошибка. ID договора: {0}\n\n{1} {2}",
id, ex.Message, ex.InnerException.Message), "Исключение - MainModel.DeleteContract");
}
stopwatch.Stop();
logger.Debug("DeleteContract(): {0}", stopwatch.Elapsed);
return result;
}
internal override List<ContractTableRow> LoadPage()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
List<ContractTableRow> source = null;
try
{
using (ModelContext model = new ModelContext())
{
Count = model.Contracts.Count();
IEnumerable<ContractTableRow> rows = model.Contracts
.OrderByDescending(r => r.Id)
.Skip(Page * Size)
.Take(Size)
.Select(c => new ContractTableRow
{
IdContract = c.Id,
Num = c.Num,
DateConclusion = c.DateConclusion.ToLongDateString(),
DateStartWork = c.DateStartWork.ToLongDateString(),
DateEndWork = c.DateEndWork.ToLongDateString(),
Salary = c.Salary,
Worker = new Item { Id = c.Worker, Value = c.WorkerNavigation.FullName },
KindWork = c.ListKindWorks.Select(y => new ItemLong { Id = y.IdKindWork, Title = y.IdKindWorkNavigation.Title, Short = y.IdKindWorkNavigation.Short }),
Subject = c.ListSubjects.Select(y => new ItemLong { Id = y.IdSubject, Title = y.IdSubjectNavigation.Title, Short = y.IdSubjectNavigation.Short })
});
source = rows.ToList();
}
}
catch (Exception ex)
{
source = null;
ShowMessage.Error(string.Format(
"Во время загрузки страницы с договорами произошла ошибка. Номер страницы: {0} (отсчет от {1})\n\n{2} {3}",
Page, MinPage, ex.Message, ex.InnerException.Message), "Исключение - MainModel.LoadPage");
}
stopwatch.Stop();
logger.Debug("Load page: {0}", stopwatch.Elapsed);
return source;
}
internal override List<ContractTableRow> Find(SearchArgs e)
{
return GetSearch.FirstPage(e);
}
internal override List<ContractTableRow> ReloadPage()
{
GetSearch.IsSearch = false;
return base.ReloadPage();
}
internal class SearchModel : SearchModel<ContractTableRow>
{
public SearchModel()
{
Size = Properties.Settings.Default.TableSearchSize;
ListTypeSearch = new List<ItemSearch>
{
new ItemSearch { Value = "Num", Display = "Номер" },
new ItemSearch { Value = "DateConclusion", Display = "Заключен" },
new ItemSearch { Value = "Worker", Display = "Работник [ФИО]" },
new ItemSearch { Value = "IdWorker", Display = "Работник [ID]"},
new ItemSearch { Value = "KindWork", Display = "Вид работы" },
new ItemSearch { Value = "Subject", Display = "Объект" },
new ItemSearch { Value = "DateStartWork", Display = "Начало" },
new ItemSearch { Value = "DateEndWork", Display = "Окончание" },
new ItemSearch { Value = "Salary", Display = "Зарплата" }
};
GetItemIdWorker = ListTypeSearch[3];
}
internal override List<ContractTableRow> LoadPage()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
string pattern = GetPattern(GetArgs.IsExactMatch, GetArgs.Keyword);
List<ContractTableRow> list = null;
try
{
using (var model = new ModelContext())
{
IQueryable<Contracts> query = model.Contracts;
if (GetArgs.ItemSearch.Value == "Num")
{
query = query
.Where(c => EF.Functions.Like(c.Num, pattern))
.Order(c => c.Num, GetArgs.SortOrder);
}
else if (GetArgs.ItemSearch.Value == "Salary")
{
query = query
.Where(c => EF.Functions.Like(c.Salary.ToString(), pattern))
.Order(c => c.Salary, GetArgs.SortOrder);
}
else if (GetArgs.ItemSearch.Value == "DateConclusion")
{
query = query
.Where(c => EF.Functions.Like(c.DateConclusion.ToLongDateString(), pattern))
.Order(c => c.DateConclusion, GetArgs.SortOrder);
}
else if (GetArgs.ItemSearch.Value == "DateStartWork")
{
query = query
.Where(c => EF.Functions.Like(c.DateStartWork.ToLongDateString(), pattern))
.Order(c => c.DateStartWork, GetArgs.SortOrder);
}
else if (GetArgs.ItemSearch.Value == "DateEndWork")
{
query = query
.Where(c => EF.Functions.Like(c.DateEndWork.ToLongDateString(), pattern))
.Order(c => c.DateEndWork, GetArgs.SortOrder);
}
else if (GetArgs.ItemSearch.Value == "Worker")
{
query = query
.Where(c => EF.Functions.Like(c.WorkerNavigation.FullName, pattern))
.Order(c => c.WorkerNavigation.FullName, GetArgs.SortOrder);
}
else if (GetArgs.ItemSearch.Value == "IdWorker")
{
query = query
.Where(c => EF.Functions.Like(c.WorkerNavigation.Id.ToString(), pattern))
.Order(c => c.WorkerNavigation.Id, GetArgs.SortOrder);
}
else if (GetArgs.ItemSearch.Value == "KindWork")
{
query = model.ListKindWorks
.Where(c => EF.Functions.Like(c.IdKindWorkNavigation.Short, pattern))
.Order(c => c.IdKindWorkNavigation.Short, GetArgs.SortOrder)
.Select(c => c.IdContractNavigation);
}
else if (GetArgs.ItemSearch.Value == "Subject")
{
query = model.ListSubjects
.Where(c => EF.Functions.Like(c.IdSubjectNavigation.Short, pattern))
.Order(c => c.IdSubjectNavigation.Short, GetArgs.SortOrder)
.Select(c => c.IdContractNavigation);
}
Count = query.Count();
list = query
.Skip(Page * Size)
.Take(Size)
.Select(c => new ContractTableRow
{
IdContract = c.Id,
Num = c.Num,
DateConclusion = c.DateConclusion.ToLongDateString(),
DateStartWork = c.DateStartWork.ToLongDateString(),
DateEndWork = c.DateEndWork.ToLongDateString(),
Salary = c.Salary,
Worker = new Item { Id = c.Worker, Value = c.WorkerNavigation.FullName },
KindWork = c.ListKindWorks.Select(y => new ItemLong { Id = y.IdKindWork, Title = y.IdKindWorkNavigation.Title, Short = y.IdKindWorkNavigation.Short }),
Subject = c.ListSubjects.Select(y => new ItemLong { Id = y.IdSubject, Title = y.IdSubjectNavigation.Title, Short = y.IdSubjectNavigation.Short })
}).ToList();
}
}
catch (Exception ex)
{
ShowMessage.Error(string.Format("Во время поиска произошла ошибка.\n\n{0} {1}",
ex.Message, ex.InnerException.Message));
}
stopwatch.Stop();
logger.Debug("Поиск договора: {0}", stopwatch.Elapsed);
return list;
}
}
}
public class WorkerModel : TableModel<WorkerTableRow>
{
internal SearchModel GetSearch { get; set; }
public WorkerModel()
{
Size = Properties.Settings.Default.TableWorkerSize;
GetSearch = new SearchModel();
}
internal override bool Delete(int id)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
bool result = true;
try
{
using (ModelContext model = new ModelContext())
{
Workers w = model.Workers.Single(x => x.Id == id);
model.Remove(w);
model.SaveChanges();
}
Count--;
}
catch (Exception ex)
{
result = false;
ShowMessage.Error(string.Format(
"Во время удаления данных работника из базы данных произошла ошибка. ID работника: {0}\n\n{1} {2}",
id, ex.Message, ex.InnerException.Message), "Исключение - MainModel.DeleteContract");
}
stopwatch.Stop();
logger.Debug("DeleteWorker(): {0}", stopwatch.Elapsed);
return result;
}
internal override List<WorkerTableRow> LoadPage()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
List<WorkerTableRow> source = null;
try
{
using (ModelContext model = new ModelContext())
{
Count = model.Workers.Count();
IEnumerable<WorkerTableRow> rows = model.Workers
.OrderByDescending(r => r.Id)
.Skip(Page * Size)
.Take(Size)
.Select(w => new WorkerTableRow
{
IdWorker = w.Id,
FullName = w.FullName,
PassportNumber = w.PassportNumber,
PassportSeries = w.PassportSeries,
Address = w.Address,
BankAccount = w.BankAccount,
DateIssued = w.DateIssued.ToLongDateString(),
Issued = new ItemLong { Id = w.IssuedNavigation.Id, Short = w.IssuedNavigation.ShortTitle, Title = w.IssuedNavigation.Title },
BankItem = new ItemLong { Id = w.BankNavigation.Id, Short = w.BankNavigation.ShortTitle, Title = w.BankNavigation.Title },
});
source = rows.ToList();
}
}
catch (Exception ex)
{
source = null;
ShowMessage.Error(string.Format(
"Во время загрузки страницы с данными работников произошла ошибка. Номер страницы: {0} (отсчет от {1})\n\n{2} {3}",
Page, MinPage, ex.Message, ex.InnerException.Message), "Исключение - MainModel.LoadPage");
}
stopwatch.Stop();
logger.Debug("Load page: {0}", stopwatch.Elapsed);
return source;
}
internal override List<WorkerTableRow> Find(SearchArgs e)
{
return GetSearch.FirstPage(e);
}
internal override List<WorkerTableRow> ReloadPage()
{
GetSearch.IsSearch = false;
return base.ReloadPage();
}
internal class SearchModel : SearchModel<WorkerTableRow>
{
public SearchModel()
{
Size = Properties.Settings.Default.TableSearchSize;
ListTypeSearch = new List<ItemSearch>
{
new ItemSearch { Value = "Id", Display = "ID" },
new ItemSearch { Value = "FullName", Display = "ФИО" },
new ItemSearch { Value = "PassportSeries", Display = "Серия паспорта" },
new ItemSearch { Value = "PassportNumber", Display = "Личный номер" },
new ItemSearch { Value = "DateIssued", Display = "Дата выдачи" },
new ItemSearch { Value = "Address", Display = "Адрес" },
new ItemSearch { Value = "BankAccount", Display = "Счет" }
};
GetItemIdWorker = ListTypeSearch[0];
}
internal override List<WorkerTableRow> LoadPage()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
string pattern = GetPattern(GetArgs.IsExactMatch, GetArgs.Keyword);
List<WorkerTableRow> list = null;
try
{
using (ModelContext model = new ModelContext())
{
IQueryable<Workers> query = model.Workers;
if (GetArgs.ItemSearch.Value == "Id")
query = query
.Where(w => EF.Functions.Like(w.Id.ToString(), pattern))
.Order(w=>w.Id, GetArgs.SortOrder);
else if (GetArgs.ItemSearch.Value == "FullName")
query = query
.Where(w => EF.Functions.Like(w.FullName, pattern))
.Order(w=>w.FullName, GetArgs.SortOrder);
else if (GetArgs.ItemSearch.Value == "PassportSeries")
query = query
.Where(w => EF.Functions.Like(w.PassportSeries, pattern))
.Order(w => w.PassportSeries, GetArgs.SortOrder);
else if (GetArgs.ItemSearch.Value == "PassportNumber")
query = query
.Where(w => EF.Functions.Like(w.PassportNumber, pattern))
.Order(w => w.PassportNumber, GetArgs.SortOrder);
else if (GetArgs.ItemSearch.Value == "Address")
query = query
.Where(w => EF.Functions.Like(w.Address, pattern))
.Order(w => w.Address, GetArgs.SortOrder);
else if (GetArgs.ItemSearch.Value == "BankAccount")
query = query
.Where(w => EF.Functions.Like(w.BankAccount, pattern))
.Order(w => w.BankAccount, GetArgs.SortOrder);
else if (GetArgs.ItemSearch.Value == "DateIssued")
query = query
.Where(w => EF.Functions.Like(w.DateIssued.ToLongDateString(), pattern))
.Order(w => w.DateIssued, GetArgs.SortOrder);
Count = query.Count();
list = query
.Skip(Page * Size)
.Take(Size)
.Select(w => new WorkerTableRow
{
IdWorker = w.Id,
FullName = w.FullName,
PassportNumber = w.PassportNumber,
PassportSeries = w.PassportSeries,
Address = w.Address,
BankAccount = w.BankAccount,
DateIssued = w.DateIssued.ToLongDateString(),
Issued = new ItemLong { Id = w.IssuedNavigation.Id, Short = w.IssuedNavigation.ShortTitle, Title = w.IssuedNavigation.Title },
BankItem = new ItemLong { Id = w.BankNavigation.Id, Short = w.BankNavigation.ShortTitle, Title = w.BankNavigation.Title },
}).ToList();
}
}
catch (Exception ex)
{
ShowMessage.Error(string.Format("Во время поиска произошла ошибка.\n\n{0} {1}",
ex.Message, ex.InnerException.Message));
}
stopwatch.Stop();
logger.Debug("Поиск работника: {0}", stopwatch.Elapsed);
return list;
}
}
}
public abstract class TableModel<T> : PagedModelBase<T>
{
internal void Added()
{
Count++;
}
internal abstract bool Delete(int id);
internal abstract List<T> Find(SearchArgs e);
}
public abstract class SearchModel<T> : PagedModelBase<T>
{
private bool isSearch;
internal bool IsSearch
{
get { return isSearch; }
set
{
isSearch = value;
Button[2] = IsSearch;
}
}
internal ItemSearch GetItemIdWorker { get; set; }
internal List<ItemSearch> ListTypeSearch { get; set; }
internal string GetSeachResultText
{
get
{
string text;
if (Count == 0)
{
text = string.Empty;
}
else
{
text = string.Format("Найдено: {0}", Count);
}
return text;
}
}
internal List<T> FirstPage(SearchArgs e)
{
GetArgs = e;
List<T> list = FirstPage();
if (list.Count > 0)
{
IsSearch = true;
PageNumText = GetPageNumText;
ResultSearchText = GetSeachResultText;
}
return list;
}
internal string GetPattern(bool exactMatch, string text)
{
if (!exactMatch)
{
text = string.Format("%{0}%", text);
}
return text;
}
}
public abstract class PagedModelBase<T> : ToolStripUI
{
internal Logger logger = NLog.LogManager.GetCurrentClassLogger();
internal event EventHandler InnerChangePageEvent;
private int count;
private int page = -1;
private int maxPage = 1;
internal int MaxPage
{
get { return maxPage; }
set
{
maxPage = value;
if (Page >= MaxPage)
{
Page = MaxPage - 1;
InnerChangePageEvent?.Invoke(this, EventArgs.Empty);
}
else if (Page == MaxPage - 1)
{
Button[1] = false;
}
else
{
Button[1] = true;
}
}
}
internal int MinPage { get; }
internal int Count
{
get { return count; }
set
{
count = value;
if (Count > 0)
{
MaxPage = GetMaxPage();
}
PageNumText = GetPageNumText;
}
}
internal int Size { get; set; }
internal int Page
{
get { return page; }
set
{
if (value <= MinPage)
{
page = MinPage;
Button[0] = false;
}
else
{
page = value;
Button[0] = true;
}
}
}
private int GetMaxPage()
{
return Convert.ToInt32(Math.Ceiling((double)Count / Size));
}
internal string GetPageNumText
{
get { return string.Format("{0} из {1}", Page + 1, MaxPage); }
}
internal virtual List<T> ReloadPage()
{
return LoadPage();
}
internal List<T> FirstPage()
{
return CustomPage(MinPage);
}
internal List<T> LastPage()
{
return CustomPage(MaxPage - 1);
}
internal List<T> NextPage()
{
return CustomPage(Page + 1);
}
internal List<T> PreviousPage()
{
return CustomPage(Page - 1);
}
internal List<T> CustomPage(int customPage)
{
List<T> list;
Page = customPage;
list = LoadPage();
return list;
}
internal abstract List<T> LoadPage();
}
public class ToolStripUI
{
public bool[] Button { get; set; } = new bool[3];
public SearchArgs GetArgs { get; set; } = new SearchArgs(null, Properties.Settings.Default.ResultSearch_SortDirectionDefault, false, string.Empty);
public string PageNumText { get; set; }
public string ResultSearchText { get; set; } = string.Empty;
}
public class SearchArgs : EventArgs
{
public ItemSearch ItemSearch;
public SortOrder SortOrder;
public bool IsExactMatch;
public string Keyword;
public SearchArgs(ItemSearch item, SortOrder order, bool match, string keyword)
{
ItemSearch = item;
SortOrder = order;
IsExactMatch = match;
Keyword = keyword;
}
}
UPDATE
In connection with the continuation of the development, updated the classes code to the actual state. The changes affect part of the search and have almost no effect on the interaction of classes. In addition to reducing duplicate values. So in ToolStripUI
appeared one property GetArgs
, which contains data from SearchArgs
.
UPDATE
My topic don't correct? No any comment and answer on theme.
... // Likewise
- please post all code. \$\endgroup\$SearchTable*
andTable*
- are essentially the same. The difference is in referring to another table whose names differ according toContract
andWorker
\$\endgroup\$