Sharpener

Sharpener is a open source application developed in C#.Net basically its a code generator made to complement S#arp Architecture.  Instead of writing CRUD codes for each database table this project will generate it for you saving you time and effort in creating a repetitive process.

In this project I also added code generation for views and it uses Kendo UI so each database table is represented as a grid with their own views complete with a menu that exposes it as a database setting.  So what does this tool generates?

It generates the following classes for

  1. Domain
  2. Query
  3. Query Interfaces
  4. View Model (for each Related Table)
  5. View Model (for Page)
  6. Controllers
  7. Views (which exposes them as a Kendo UI Grid)
  8. Command
  9. CommandHandler (Create, Update and Delete)
  10. Site Map

So just imagine you have 20 tables in your database, usually you will code at least 180 classes if want to do it in a proper way but now you don’t have to do that, this tool will generate it for you.  Code generation might be just one of its benefits but another benefit in using Sharpener is making sure that you are following the proper naming and coding convention for your project.

So how does each class look like.  Lets say we have the following simple database structure of Employee, Position and Department, now let start.

1. Domain – Basically this is the representation of your database table exposed as a class, so if you have  the Employee Class then it will be generated like this

namespace MyWebApp.Domain
{
	using System;
	using SharpArch.Domain.DomainModel;

	public class Employee : Entity
	{
		public virtual Position Position { getset; }

		public virtual Department Department { getset; }

		public virtual string FirstName { getset; }

		public virtual string LastName { getset; }

		public virtual DateTime DateJoined { getset; }

	}
}

Take note foreign keys are exposed as a class on its own, this makes it easy to drill down to each class when you use it in your business logic.

2. Query – This will expose a select all command which looks something like this.

/* Codes Autogenerated by Sharpener */
namespace MyWebApp.Web.Mvc.Controllers.Queries.Employees
{
	#region Using Directives
	using System;
	using System.Collections.Generic;
	using Domain;
	using NHibernate.Transform;
	using SharpArch.NHibernate;
	using ViewModels.Employees; 
	#endregion

	public class EmployeesQuery : NHibernateQueryIEmployeesQuery
	{
		public IList<EmployeesViewModel> GetEmployees()
		{
			EmployeesViewModel viewModel = null;

			return Session.QueryOver<Employee>()
				.SelectList(list => list
					.Select(x => x.Id).WithAlias(() => viewModel.EmployeeId)
					.Select(x => x.Position.Id).WithAlias(() => viewModel.PositionId)
					.Select(x => x.Department.Id).WithAlias(() => viewModel.DepartmentId)
					.Select(x => x.FirstName).WithAlias(() => viewModel.FirstName)
					.Select(x => x.LastName).WithAlias(() => viewModel.LastName)
					.Select(x => x.DateJoined).WithAlias(() => viewModel.DateJoined)
					)
				.TransformUsing(Transformers.AliasToBean<EmployeesViewModel>())
				.List<EmployeesViewModel>();
		}
	}
}

3. Query Interface

/* Codes Autogenerated by Sharpener */
namespace MyWebApp.Web.Mvc.Controllers.Queries.Employees
{
	using System.Collections.Generic;
	using ViewModels.Employees;

	public interface IEmployeesQuery
	{
		IList<EmployeesViewModel> GetEmployees();
	}
}

4. View Model – which exposes your Domain for use in views

/* Codes Autogenerated by Sharpener */
namespace MyWebApp.Web.Mvc.Controllers.ViewModels.Employees
{
	using System;
	using System.ComponentModel.DataAnnotations;

	public class EmployeesViewModel
	{
		public int? EmployeeId { getset; }

		public int PositionId { getset; }

		public int DepartmentId { getset; }

		public string FirstName { getset; }

		public string LastName { getset; }

		public DateTime DateJoined { getset; }

	}
}

5. Page View Model – Which exposes every basic things you might need in your View Page

/* Codes Autogenerated by Sharpener */
namespace MyWebApp.Web.Mvc.Controllers.ViewModels.Employees
{
	using System.Collections.Generic;
	using System.Web.Mvc;
	using Domain;

	public class EmployeesPageViewModel
	{
		public IList<EmployeesViewModel> Employees { getset; }

		public IList<SelectListItem> Position { getset; }

		public IList<SelectListItem> Department { getset; }

	}
}

So foreign keys are exposed as a Ennumerable SelectListItem so it can be displayed as a drop down on your view

6. Controller – This is that class handles the events that you will be doing on your View Page.  It handles the communication with your query and tasks.

/* Codes Autogenerated by Sharpener */
namespace MyWebApp.Web.Mvc.Controllers
{
	#region Using Directives

	using System.Web.Mvc;
	using System.Linq;
	using Kendo.Mvc.Extensions;
	using Kendo.Mvc.UI;
	using Queries.Employees;
	using SharpArch.Domain.Commands;
	using SharpArch.NHibernate.Web.Mvc;
	using Tasks.Commands.Employees;
	using ViewModels.Employees; 
	using MyWebApp.Web.Mvc.Controllers.Queries.Positions; 
	using MyWebApp.Web.Mvc.Controllers.Queries.Departments; 
	#endregion

	public class EmployeesController : Controller
	{
		private readonly IEmployeesQuery employeesQuery;
		private readonly IPositionsQuery positionsQuery;
		private readonly IDepartmentsQuery departmentsQuery;
		private readonly ICommandProcessor commandProcessor;

		public EmployeesController(
			IEmployeesQuery employeesQuery,
			IPositionsQuery positionsQuery,
			IDepartmentsQuery departmentsQuery,
			ICommandProcessor commandProcessor)
		{
			this.positionsQuery = positionsQuery;
			this.departmentsQuery = departmentsQuery;
			this.employeesQuery = employeesQuery;
			this.commandProcessor = commandProcessor;
		}

		public ActionResult Index()
		{			
			var viewModel = new EmployeesPageViewModel
			{
				Position = (from x in positionsQuery.GetPositions() select new SelectListItem { Value = x.PositionId.ToString(), Text = x.Name }).ToList(),
				Department = (from x in departmentsQuery.GetDepartments() select new SelectListItem { Value = x.DepartmentId.ToString(), Text = x.Name }).ToList(),
				Employees = employeesQuery.GetEmployees()
			};

			return View(viewModel);
		}

		public ActionResult GetEmployees([DataSourceRequest]DataSourceRequest request)
		{
			var employees = employeesQuery.GetEmployees();
			var result = employees.ToDataSourceResult(request);

			return Json(result);
		}

		[HttpPost]
		[Transaction]
		public ActionResult SaveOrUpdate(EmployeesViewModel viewModel, [DataSourceRequest]DataSourceRequest request)
		{
			if (ModelState.IsValid)
			{
				var command = new SaveOrUpdateEmployeeCommand(
								viewModel.EmployeeId,
								viewModel.PositionId,
								viewModel.DepartmentId,
								viewModel.FirstName,
								viewModel.LastName,
								viewModel.DateJoined
								);

				if (ModelState.IsValid)
				{
					commandProcessor.Process(command);
					viewModel.EmployeeId = command.EmployeeId;
				}
			}

			var result = new[] { viewModel }.ToDataSourceResult(request, ModelState);

			return Json(result);
		}		

		[HttpPost]
		[Transaction]
		public ActionResult Delete(int employeeId, [DataSourceRequest]DataSourceRequest request)
		{
			var command = new DeleteEmployeeCommand(employeeId);
			commandProcessor.Process(command);

			return Json(ModelState.ToDataSourceResult());
		}
	}
}

7. View – Using Kendo UI the database table is now exposed as an AJAX grid with CRUD functions

<!-- Codes Autogenerated by Sharpener -->
@model MyWebApp.Web.Mvc.Controllers.ViewModels.Employees.EmployeesPageViewModel
@{
	ViewBag.Title = "Employees";
	Layout = "~/Views/Shared/_Layout.cshtml";
}

<h1>Employees</h1>

@(Html.Kendo().Grid(Model.Employees)
	.Name("grid-employees")
	.Sortable()
	.Scrollable()
	.Editable(e => e.Mode(GridEditMode.InLine))   
	.ToolBar(commands => commands.Create()) 
	.DataSource(d => d.Ajax()  
		.Create("SaveOrUpdate""Employees")
		.Update("SaveOrUpdate""Employees")
		.Destroy("Delete""Employees")
		.Read("GetEmployees""Employees")
		.Model(x => x.Id(p => p.EmployeeId))
		)
	.Columns(c =>
	{
		c.Command(commands =>
		{
			commands.Edit();
			commands.Destroy();
		}).Width(150);

		c.ForeignKey(x => x.PositionId, Model.Position, "Value""Text");
		c.ForeignKey(x => x.DepartmentId, Model.Department, "Value""Text");
		c.Bound(x => x.FirstName);
		c.Bound(x => x.LastName);
		c.Bound(x => x.DateJoined).Format("{0:dd/MM/yyyy}");
		c.Bound(x => x.EmployeeId).Hidden();
	}) 
)

This also formats your datatype to the relative format needed.  Ie. Date as Date, Foreign Keys as Drop Down, Booleans as Checkboxes, etc

8. Command – This is your interface for the Command Handlers

Save and Update Command

/* Codes Autogenerated by Sharpener */
namespace MyWebApp.Tasks.Commands.Employees
{
	#region Using Directives
	using System;
	using System.Collections.Generic;
	using System.ComponentModel.DataAnnotations;
	using Domain;
	using Microsoft.Practices.ServiceLocation;
	using SharpArch.Domain.Commands;
	using SharpArch.NHibernate.Contracts.Repositories;
	#endregion

	public class SaveOrUpdateEmployeeCommand : CommandBase
	{
		public SaveOrUpdateEmployeeCommand(
			int? employeeId,
			int positionId,
			int departmentId,
			string firstName,
			string lastName,
			DateTime dateJoined
			)
		{
			this.EmployeeId = employeeId;
			this.PositionId = positionId;
			this.DepartmentId = departmentId;
			this.FirstName = firstName;
			this.LastName = lastName;
			this.DateJoined = dateJoined;
		}

		public int? EmployeeId { getset; }
		public int PositionId { getset; }
		public int DepartmentId { getset; }
		public string FirstName { getset; }
		public string LastName { getset; }
		public DateTime DateJoined { getset; }
	}
}

Delete Command

/* Codes Autogenerated by Sharpener */
namespace MyWebApp.Tasks.Commands.Employees
{
	#region Using Directives
	using Domain;
	using SharpArch.Domain.Commands; 
	#endregion

	public class DeleteEmployeeCommand : CommandBase
	{
		public DeleteEmployeeCommand(int employeeId)
		{
			this.EmployeeId = employeeId;
		}

		public int EmployeeId { getset; }
	}
}

9. Command Handler – This performs the actual task

Save Update Command Handler

/* Codes Autogenerated by Sharpener */
namespace MyWebApp.Tasks.CommandHandlers.Employees
{
	#region Using Directives
	using System;
	using Commands.Employees;
	using Domain;
	using SharpArch.Domain.Commands;
	using SharpArch.NHibernate.Contracts.Repositories; 
	#endregion

	public class SaveOrUpdateEmployeeCommandHandler : ICommandHandler<SaveOrUpdateEmployeeCommand>
	{
		private readonly INHibernateRepository<Position> positionRepository;

		private readonly INHibernateRepository<Department> departmentRepository;

		private readonly INHibernateRepository<Employee> employeeRepository;

		public SaveOrUpdateEmployeeCommandHandler(
			INHibernateRepository<Position> positionRepository,
			INHibernateRepository<Department> departmentRepository,
			INHibernateRepository<Employee> employeeRepository)
		{
			this.positionRepository = positionRepository;
			this.departmentRepository = departmentRepository;
			this.employeeRepository = employeeRepository;
		}

		public void Handle(SaveOrUpdateEmployeeCommand command)
		{			
			var employee = command.EmployeeId.HasValue
				? employeeRepository.Get(command.EmployeeId.Value)
				: new Employee();

			employee.Position = positionRepository.Get(command.PositionId);
			employee.Department = departmentRepository.Get(command.DepartmentId);
			employee.FirstName = command.FirstName;
			employee.LastName = command.LastName;
			employee.DateJoined = command.DateJoined;

			employeeRepository.SaveOrUpdate(employee);

			command.EmployeeId = employee.Id;
		}
	}
}

Delete Command Handler

/* Codes Autogenerated by Sharpener */
namespace MyWebApp.Tasks.CommandHandlers.Employees
{
	#region Using Directives
	using Commands.Employees;
	using Domain;
	using SharpArch.Domain.Commands;
	using SharpArch.NHibernate.Contracts.Repositories; 
	#endregion

	public class DeleteEmployeeCommandHandler : ICommandHandler<DeleteEmployeeCommand>
	{
		private readonly INHibernateRepository<Employee> employeeRepository;

		public DeleteEmployeeCommandHandler(INHibernateRepository<Employee> employeeRepository)
		{
			this.employeeRepository = employeeRepository;
		}

		public void Handle(DeleteEmployeeCommand command)
		{
			var employee = employeeRepository.Get(command.EmployeeId);
			if (employee != null)
			{
				employeeRepository.Delete(employee);
			}
		}
	}
}

10.  Site Map – This exposes all of the Views generated as a Application Setting

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode title="Home" >
<siteMapNode title="Settings" >
  <siteMapNode title="Department" controller="Departments" action="Index" />
  <siteMapNode title="Employee" controller="Employees" action="Index" />
  <siteMapNode title="Position" controller="Positions" action="Index" />
</siteMapNode>
</siteMapNode>
</siteMap>

Usage

Now to use this applicaiton, you need to use S#arp Architecture and Kendo UI (though it can be omitted), if not I have written a step by step process of using them here (http://www.macaalay.com/2013/06/21/guide-in-using-kendo-ui-with-sarp-architecture/).

Next is to create your database but if you have one already make sure they are named properly and have primary and foreign keys properly created, see guide on link above for conventions.  So lets start, lets say you have a simple Employee, Department and Position database like such.

00 Database Diagram

Run the application and define your parametes, such as connection string, your project namespace and what objects to generate.  Please take not if you have an exisiting class with the same name it would overwrite it.

01 Set parameters

Now it will ask you in what folder you want to save those files, choose your project folder root.

02 Choose project folder

Once finished you will have a popup message saying so

03 Finish

Now open your S#arp project and show all the files

04 Show all Files

You will now see all of the generated files by Sharpener, all of it will appear in the Domain, Insfrastructure and Task Layers

05 Generated Files

06 Generated Files

Now include all of them in your project

07 Include in project

Run the project then you will see a whole CRUD process using Kendo UI.

08 Running the codes

Downloads

Currently On Beta version:
Source Code : https://sourceforge.net/projects/sharpener/files/v1.0 Beta/Source Code/
Executable : https://sourceforge.net/projects/sharpener/files/v1.0 Beta/

4 thoughts on “Sharpener

  1. Pingback: Sharpener – My First Open Source | Raymund Macaalay's Dev Blog

  2. Pingback: Using Kendo UI 2013.2 with S#arp Architecture 2.1.2 | Raymund Macaalay's Dev Blog

  3. Pingback: Creating a Solid Architectural Foundation from scratch is not really that hard | Raymund Macaalay's Dev Blog

Leave a Reply