Effinator is a open source application developed in C#.Net basically its a code generator to build your Basic Architecture based on Entity Framework this work well with the architecture structure I explained on my article earlier so its best to read that first for a better understand. But in a gist, with this tool 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 which will have the same look and feel as the default template of ASP.Net MVC on Visual Studio 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
- Repositories with Interfaces
- Database Factory
- Unit of Work
- Generic Repositories
- Extended Repositories
- Query with Interfaces
- View Models
- Individual Entity
- Collection of Entities
- Related Entities
- Controllers
- Views
- Create
- Delete
- Details
- Edit
- Index (Grid View)
- Dependecy Injection
- Commands
- Command Processor
- Command
- Delete
- Save or Update
- CommandHandler
- Delete
- Save or Update
- Site Map
So just imagine you have 20 tables in your database, usually you will code at least 340 classes and interfaces if want to do it in a proper way but now you don’t have to do that, this tool will generate that 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.
Lets say we have the following simple database structure below as an example to set things into perspective.
So how does each class and interface look like? How does each file look like? Let’s separate them by Layers so its easier to understand.
The Infrastructure Layer
In this layer lets start with the generics
DatabaseFactory.cs – This is the class that will connect to your Data Context, so in short it contains factory methods for creating Database objects.
using Employees.Domain; using Employees.Infrastructure.Repositories.Interfaces; namespace Employees.Infrastructure.Repositories { public class DatabaseFactory : Disposable, IDatabaseFactory { private EmployeesEntities _dataContext; public EmployeesEntities Get() { return _dataContext ?? (_dataContext = new EmployeesEntities()); } protected override void DisposeCore() { if (_dataContext != null) _dataContext.Dispose(); } } }
Disposable.cs – This is for Garbage Collection, making sure we clear up the resources used.
namespace Employees.Infrastructure.Repositories { using System; public class Disposable : IDisposable { private bool _isDisposed; ~Disposable() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!_isDisposed && disposing) { DisposeCore(); } _isDisposed = true; } protected virtual void DisposeCore() { } } }
GenericRepository.cs – This class contains all the generic methods that you can perform for your entities, methods like Add, Update and Delete.
using System; using System.Collections.Generic; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Linq; using System.Linq.Expressions; using Employees.Domain; using Employees.Infrastructure.Repositories.Interfaces; namespace Employees.Infrastructure.Repositories { public class GenericRepository<T> : IGenericRepository<T> where T : class { private EmployeesEntities _dataContext; private readonly IDbSet<T> _dbset; protected GenericRepository(IDatabaseFactory databaseFactory) { DatabaseFactory = databaseFactory; _dbset = DataContext.Set<T>(); } protected IDatabaseFactory DatabaseFactory { get; private set; } protected EmployeesEntities DataContext { get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); } } public virtual void AddOrUpdate(T entity) { _dbset.AddOrUpdate(entity); } public virtual void Add(T entity) { _dbset.Add(entity); } public virtual void Update(T entity) { _dbset.Attach(entity); _dataContext.Entry(entity).State = EntityState.Modified; } public virtual void Delete(T entity) { _dbset.Remove(entity); } public virtual void Delete(Expression<Func<T, bool>> where) { IEnumerable<T> objects = _dbset.Where<T>(where).AsEnumerable(); foreach (T obj in objects) _dbset.Remove(obj); } public virtual T Get(int id) { return _dbset.Find(id); } public virtual IEnumerable<T> GetAll() { return _dbset.ToList(); } public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where) { return _dbset.Where(where).ToList(); } public T GetBy(Expression<Func<T, bool>> where) { return _dbset.Where(where).FirstOrDefault<T>(); } } }
UnitOfWork.cs – The unit of work class coordinates the work of different multiple repositories by creating a single database context class shared by all of them.
using Employees.Domain; using Employees.Infrastructure.Repositories.Interfaces; namespace Employees.Infrastructure.Repositories { public class UnitOfWork : IUnitOfWork { private readonly IDatabaseFactory _databaseFactory; private EmployeesEntities _dataContext; public UnitOfWork(IDatabaseFactory databaseFactory) { this._databaseFactory = databaseFactory; } protected EmployeesEntities DataContext { get { return _dataContext ?? (_dataContext = _databaseFactory.Get()); } } public void Commit() { DataContext.SaveChanges(); } } }
And their interfaces
IDatabaseFactory.cs
using System; using Employees.Domain; namespace Employees.Infrastructure.Repositories.Interfaces { public interface IDatabaseFactory : IDisposable { EmployeesEntities Get(); } }
IGenericRepository.cs
using System; using System.Collections.Generic; using System.Linq.Expressions; namespace Employees.Infrastructure.Repositories.Interfaces { public interface IGenericRepository<T> where T : class { void AddOrUpdate(T entity); void Add(T entity); void Update(T entity); void Delete(T entity); void Delete(Expression<Func<T, bool>> where); T Get(int Id); T GetBy(Expression<Func<T, bool>> where); IEnumerable<T> GetAll(); IEnumerable<T> GetMany(Expression<Func<T, bool>> where); } }
IUnitOfWork.cs
namespace Employees.Infrastructure.Repositories.Interfaces { public interface IUnitOfWork { void Commit(); } }
Now lets go to the specific repositories, for this example we will just show the Employees Entity
EmployeeRepository.cs
namespace Employees.Infrastructure.ExtendedRepositories.Employees { using Domain; using Repositories; using Repositories.Interfaces; public class EmployeeRepository : GenericRepository<Employee>, IEmployeeRepository { public EmployeeRepository(IDatabaseFactory databaseFactory) : base(databaseFactory) { } } }
IEmployeeRepository.cs
namespace Employees.Infrastructure.ExtendedRepositories.Employees { using Domain; using Repositories.Interfaces; public interface IEmployeeRepository : IGenericRepository<Employee> { } }
The Framework Layer
Here we just have one file generated and its used for Validation Results
namespace Employees.Framework.Common { public class ValidationResult { public ValidationResult() { } public ValidationResult(string memeberName, string message) { MemberName = memeberName; Message = message; } public ValidationResult(string message) { Message = message; } public string MemberName { get; set; } public string Message { get; set; } } }
The Task Layer
CommandHandlerNotFoundException.cs – This class will take care of non existent command handlers.
using System; namespace Employees.Tasks.Base { public class CommandHandlerNotFoundException : Exception { public CommandHandlerNotFoundException(Type type) : base(string.Format("Command handler not found for command type: {0}", type)) { } } }
CommandProcessor.cs – This class is responsible for processing commands
using System.Collections.Generic; using System.Web.Mvc; using Employees.Framework.Common; using Employees.Tasks.Base.Interface; namespace Employees.Tasks.Base { public class CommandProcessor : ICommandProcessor { public ICommandResult Process<TCommand>(TCommand command) where TCommand: ICommand { var handler = DependencyResolver.Current.GetService<ICommandHandler<TCommand>>(); if (handler == null) { throw new CommandHandlerNotFoundException(typeof(TCommand)); } return handler.Execute(command); } public IEnumerable<ValidationResult> Validate<TCommand>(TCommand command) where TCommand : ICommand { var handler = DependencyResolver.Current.GetService<IValidationHandler<TCommand>>(); if (handler == null) { throw new ValidationHandlerNotFoundException(typeof(TCommand)); } return handler.Validate(command); } } }
CommandResult.cs – This class will handle result executed from the Command Handler
using Employees.Tasks.Base.Interface; namespace Employees.Tasks.Base { public class CommandResult : ICommandResult { public CommandResult(bool success) { Success = success; } public bool Success { get; protected set; } } }
CommandResults.cs – Similar with the Command Result but this is more for a collection.
using System.Collections.Generic; using System.Linq; using Employees.Tasks.Base.Interface; namespace Employees.Tasks.Base { public class CommandResults : ICommandResults { private readonly List<ICommandResult> _results = new List<ICommandResult>(); public void AddResult(ICommandResult result) { _results.Add(result); } public ICommandResult[] Results { get { return _results.ToArray(); } } public bool Success { get { return _results.All<ICommandResult>(result => result.Success); } } } }
ValidationHandlerNotFoundException.cs – Takes care of any Validation Handler that is not found.
using System; namespace Employees.Tasks.Base { public class ValidationHandlerNotFoundException : Exception { public ValidationHandlerNotFoundException(Type type) : base(string.Format("Validation handler not found for command type: {0}", type)) { } } }
Now for the interfaces
ICommand.cs
namespace Employees.Tasks.Base.Interface { public interface ICommand { } }
ICommandHandler.cs
namespace Employees.Tasks.Base.Interface { public interface ICommandHandler<in TCommand> where TCommand: ICommand { ICommandResult Execute(TCommand command); } }
ICommandProcessor.cs
using System.Collections.Generic; using Employees.Framework.Common; namespace Employees.Tasks.Base.Interface { public interface ICommandProcessor { ICommandResult Process<TCommand>(TCommand command) where TCommand : ICommand; IEnumerable<ValidationResult> Validate<TCommand>(TCommand command) where TCommand : ICommand; } }
ICommandResult.cs
namespace Employees.Tasks.Base.Interface { public interface ICommandResult { bool Success { get; } } }
ICommandResults.cs
namespace Employees.Tasks.Base.Interface { public interface ICommandResults { ICommandResult[] Results { get; } bool Success { get; } } }
IValidationHandler.cs
using System.Collections.Generic; using Employees.Framework.Common; namespace Employees.Tasks.Base.Interface { public interface IValidationHandler<in TCommand> where TCommand : ICommand { IEnumerable<ValidationResult> Validate(TCommand command); } }
Now for the specific Delete, Update and Add Commands and CommandHandlers for each entity.
DeleteEmployeeCommand.cs – Used for deleting entities
namespace Employees.Tasks.Commands.Employees { #region Using Directives using Base.Interface; #endregion public class DeleteEmployeeCommand : ICommand { public DeleteEmployeeCommand(int employeeId) { EmployeeId = employeeId; } public int EmployeeId { get; set; } } }
SaveOrUpdateEmployeeCommand.cs – Used for Saving and Updating Entities.
namespace Employees.Tasks.Commands.Employees { #region Using Directives using System; using Base.Interface; #endregion public class SaveOrUpdateEmployeeCommand : ICommand { public SaveOrUpdateEmployeeCommand( int? employeeId, string firstName, string lastName, int departmentId, int positionId, int? employmentStatusTypeId, DateTime? birthdate, bool? isActive ) { EmployeeId = employeeId; FirstName = firstName; LastName = lastName; DepartmentId = departmentId; PositionId = positionId; EmploymentStatusTypeId = employmentStatusTypeId; Birthdate = birthdate; IsActive = isActive; } public int? EmployeeId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public int DepartmentId { get; set; } public int PositionId { get; set; } public int? EmploymentStatusTypeId { get; set; } public DateTime? Birthdate { get; set; } public bool? IsActive { get; set; } } }
DeleteEmployeeCommandHandler.cs – The handler for the Delete Command.
namespace Employees.Tasks.CommandHandlers.Employees { #region Using Directives using Domain; using Infrastructure.Repositories.Interfaces; using Base; using Base.Interface; using Commands.Employees; #endregion public class DeleteEmployeeCommandHandler : ICommandHandler<DeleteEmployeeCommand> { private readonly IGenericRepository<Employee> _employeeRepository; private readonly IUnitOfWork _unitOfWork; public DeleteEmployeeCommandHandler( IGenericRepository<Employee> employeeRepository, IUnitOfWork unitOfWork ) { _employeeRepository = employeeRepository; _unitOfWork = unitOfWork; } public ICommandResult Execute(DeleteEmployeeCommand command) { var employee = _employeeRepository.Get(command.EmployeeId); if (employee != null) { _employeeRepository.Delete(employee); } _unitOfWork.Commit(); return new CommandResult(true); } } }
SaveOrUpdateEmployeeCommandHandler.cs – The handler for Save and Update Command
namespace Employees.Tasks.CommandHandlers.Employees { #region Using Directives using Domain; using Infrastructure.Repositories.Interfaces; using Base; using Base.Interface; using Commands.Employees; #endregion public class SaveOrUpdateEmployeeCommandHandler : ICommandHandler<SaveOrUpdateEmployeeCommand> { private readonly IGenericRepository<Department> _departmentRepository; private readonly IGenericRepository<Position> _positionRepository; private readonly IGenericRepository<EmploymentStatusType> _employmentStatusTypeRepository; private readonly IGenericRepository<Employee> _employeeRepository; private readonly IUnitOfWork _unitOfWork; public SaveOrUpdateEmployeeCommandHandler( IGenericRepository<Department> departmentRepository, IGenericRepository<Position> positionRepository, IGenericRepository<EmploymentStatusType> employmentStatusTypeRepository, IGenericRepository<Employee> employeeRepository, IUnitOfWork unitOfWork) { _departmentRepository = departmentRepository; _positionRepository = positionRepository; _employmentStatusTypeRepository = employmentStatusTypeRepository; _employeeRepository = employeeRepository; _unitOfWork = unitOfWork; } public ICommandResult Execute(SaveOrUpdateEmployeeCommand command) { var employee = command.EmployeeId.HasValue ? _employeeRepository.Get(command.EmployeeId.Value) : new Employee(); employee.FirstName = command.FirstName; employee.LastName = command.LastName; employee.Department = _departmentRepository.Get(command.DepartmentId); employee.Position = _positionRepository.Get(command.PositionId); if (command.EmploymentStatusTypeId != null) employee.EmploymentStatusType = _employmentStatusTypeRepository.Get(command.EmploymentStatusTypeId.Value); employee.Birthdate = command.Birthdate; employee.IsActive = command.IsActive; _employeeRepository.AddOrUpdate(employee); _unitOfWork.Commit(); command.EmployeeId = employee.EmployeeId; return new CommandResult(true); } } }
The Presentation Layer
Lets start with the Queries
EmployeesQuery.cs – The generator creates the usual Get, GetAll and GetForeignKeys Methods
namespace Employees.Web.Controllers.Queries.Employees { #region Using Directives using System.Collections.Generic; using System.Linq; using Domain; using Infrastructure.Repositories.Interfaces; using System.Web.Mvc; using ViewModels.Employees; #endregion public class EmployeesQuery : IEmployeesQuery { private readonly IGenericRepository<Department> _departmentsRepository; private readonly IGenericRepository<Position> _positionsRepository; private readonly IGenericRepository<EmploymentStatusType> _employmentStatusTypesRepository; private readonly IGenericRepository<Employee> _employeesRepository; public EmployeesQuery( IGenericRepository<Department> departmentsRepository, IGenericRepository<Position> positionsRepository, IGenericRepository<EmploymentStatusType> employmentStatusTypesRepository, IGenericRepository<Employee> employeesRepository) { _employeesRepository = employeesRepository; _departmentsRepository = departmentsRepository; _positionsRepository = positionsRepository; _employmentStatusTypesRepository = employmentStatusTypesRepository; } public EmployeesViewModel Get(int id) { var employee = _employeesRepository.Get(id); if (employee == null) return null; var viewModel = new EmployeesViewModel { EmployeeId = employee.EmployeeId, FirstName = employee.FirstName, LastName = employee.LastName, DepartmentId = employee.DepartmentId, PositionId = employee.PositionId, EmploymentStatusTypeId = employee.EmploymentStatusTypeId, Birthdate = employee.Birthdate, IsActive = employee.IsActive, }; viewModel.EmployeesForeignKeysViewModel = GetForeignKeysViewModel(); return viewModel; } public EmployeesGridViewModel GetAll() { var viewModel = new EmployeesGridViewModel(); var employees = (from employee in _employeesRepository.GetAll() select new EmployeesViewModel { EmployeeId = employee.EmployeeId, FirstName = employee.FirstName, LastName = employee.LastName, DepartmentId = employee.DepartmentId, PositionId = employee.PositionId, EmploymentStatusTypeId = employee.EmploymentStatusTypeId, Birthdate = employee.Birthdate, IsActive = employee.IsActive, }).ToList(); viewModel.Employees = employees; viewModel.EmployeesForeignKeysViewModel = GetForeignKeysViewModel(); return viewModel; } public EmployeesForeignKeysViewModel GetForeignKeysViewModel() { var viewModel = new EmployeesForeignKeysViewModel { Departments = (from x in _departmentsRepository.GetAll() select new SelectListItem {Value = x.DepartmentId.ToString(), Text = x.Name}).ToList(), Positions = (from x in _positionsRepository.GetAll() select new SelectListItem {Value = x.PositionId.ToString(), Text = x.Name}).ToList(), EmploymentStatusTypes = (from x in _employmentStatusTypesRepository.GetAll() select new SelectListItem {Value = x.EmploymentStatusTypeId.ToString(), Text = x.Name}).ToList(), }; return viewModel; } } }
IEmployeesQuery.cs
namespace Employees.Web.Controllers.Queries.Employees { using System.Collections.Generic; using ViewModels.Employees; public interface IEmployeesQuery { EmployeesViewModel Get(int id); EmployeesGridViewModel GetAll(); EmployeesForeignKeysViewModel GetForeignKeysViewModel(); } }
The View Models is divided into 3 types, one for individual ViewModel to Entity Mapping, another for use in Grids or any Collections, and the third one is a sub ViewModel for Foreign Keys.
EmployeesViewModel.cs – For a single Entity with attached Foreign keys
namespace Employees.Web.Controllers.ViewModels.Employees { using System; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Collections.Generic; using System.Web.Mvc; public class EmployeesViewModel { [DisplayName("Employee Id")] public int? EmployeeId { get; set; } [DisplayName("First Name")] [MaxLength(50)] [Required] public string FirstName { get; set; } [DisplayName("Last Name")] [MaxLength(50)] [Required] public string LastName { get; set; } [DisplayName("Department Id")] [Required] public int DepartmentId { get; set; } [DisplayName("Position Id")] [Required] public int PositionId { get; set; } [DisplayName("Employment Status Type Id")] public int? EmploymentStatusTypeId { get; set; } [DisplayName("Birthdate")] [DataType(DataType.Date)] public DateTime? Birthdate { get; set; } [DisplayName("Is Active")] public bool? IsActive { get; set; } public EmployeesForeignKeysViewModel EmployeesForeignKeysViewModel { get; set; } }
EmployeesGridViewModel.cs – For a multiple similar Entity with attached Foreign keys
namespace Employees.Web.Controllers.ViewModels.Employees { using System; using System.Collections.Generic; using System.Web.Mvc; public class EmployeesGridViewModel { public IList<EmployeesViewModel> Employees { get; set; } public EmployeesForeignKeysViewModel EmployeesForeignKeysViewModel { get; set; } } }
EmployeesForeignKeysViewModel.cs – Model for foreign keys that are exposed as a Collection of SelectListItem so we can easily use them for drop downs.
namespace Employees.Web.Controllers.ViewModels.Employees { using System.Collections.Generic; using System.Web.Mvc; public class EmployeesForeignKeysViewModel { public IList<SelectListItem> Departments { get; set; } public IList<SelectListItem> Positions { get; set; } public IList<SelectListItem> EmploymentStatusTypes { get; set; } } }
Now lets go to the controllers, this one will be straightforward it will be nearly as similar as the default ASP.Net MVC Template.
EmployeesController.cs – All of the common Actions are implemented like Index, Details, Create, Edit and Delete separated by POST and GET request.
namespace Employees.Web.Controllers { #region Using Directives using System.Net; using System.Web.Mvc; using Tasks.Base.Interface; using Tasks.Commands.Employees; using Queries.Employees; using Web.Controllers.ViewModels.Employees; using Queries.Departments; using Queries.Positions; using Queries.EmploymentStatusTypes; #endregion public class EmployeesController : Controller { private readonly IEmployeesQuery _employeesQuery; private readonly IDepartmentsQuery _departmentsQuery; private readonly IPositionsQuery _positionsQuery; private readonly IEmploymentStatusTypesQuery _employmentStatusTypesQuery; private readonly ICommandProcessor _commandProcessor; public EmployeesController( IEmployeesQuery employeesQuery, IDepartmentsQuery departmentsQuery, IPositionsQuery positionsQuery, IEmploymentStatusTypesQuery employmentStatusTypesQuery, ICommandProcessor commandProcessor) { _departmentsQuery = departmentsQuery; _positionsQuery = positionsQuery; _employmentStatusTypesQuery = employmentStatusTypesQuery; _employeesQuery = employeesQuery; _commandProcessor = commandProcessor; } // GET: Employees public ActionResult Index() { var viewModel = _employeesQuery.GetAll(); return View(viewModel); } // GET: Employees/Details/1 public ActionResult Details(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } var viewModel = _employeesQuery.Get(id.Value); if (viewModel == null) { return HttpNotFound(); } return View(viewModel); } // GET: Employees/Create public ActionResult Create() { var viewModel = new EmployeesViewModel(); viewModel.EmployeesForeignKeysViewModel = _employeesQuery.GetForeignKeysViewModel(); return View(viewModel); } // POST: Employees/Create // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(EmployeesViewModel viewModel) { if (ModelState.IsValid) { var command = new SaveOrUpdateEmployeeCommand( viewModel.EmployeeId, viewModel.FirstName, viewModel.LastName, viewModel.DepartmentId, viewModel.PositionId, viewModel.EmploymentStatusTypeId, viewModel.Birthdate, viewModel.IsActive ); if (ModelState.IsValid) { _commandProcessor.Process(command); viewModel.EmployeeId = command.EmployeeId; } return RedirectToAction("Index"); } return View(viewModel); } // GET: Employees/Edit/1 public ActionResult Edit(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } var viewModel = _employeesQuery.Get(id.Value); if (viewModel == null) { return HttpNotFound(); } return View(viewModel); } // POST: Employees/Edit/1 // To protect from overposting attacks, please enable the specific properties you want to bind to, for // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit(EmployeesViewModel viewModel) { if (ModelState.IsValid) { var command = new SaveOrUpdateEmployeeCommand( viewModel.EmployeeId, viewModel.FirstName, viewModel.LastName, viewModel.DepartmentId, viewModel.PositionId, viewModel.EmploymentStatusTypeId, viewModel.Birthdate, viewModel.IsActive ); if (ModelState.IsValid) { _commandProcessor.Process(command); viewModel.EmployeeId = command.EmployeeId ; } return RedirectToAction("Index"); } return View(viewModel); } // GET: Employees/Delete/1 public ActionResult Delete(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } var viewModel = _employeesQuery.Get(id.Value); if (viewModel == null) { return HttpNotFound(); } return View(viewModel); } // POST: Employees/Delete/1 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public ActionResult DeleteConfirmed(int id) { var command = new DeleteEmployeeCommand(id); _commandProcessor.Process(command); return RedirectToAction("Index"); } } }
Then lets go to the views
Create.cshtml – To add new entity to the database
@model Employees.Web.Controllers.ViewModels.Employees.EmployeesViewModel @{ ViewBag.Title = "Create"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>Create</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Employee</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(model => model.FirstName, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.LastName, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.DepartmentId, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.DepartmentId, Model.EmployeesForeignKeysViewModel.Departments, "", new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.DepartmentId, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.PositionId, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.PositionId, Model.EmployeesForeignKeysViewModel.Positions, "", new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.PositionId, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.EmploymentStatusTypeId, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.EmploymentStatusTypeId, Model.EmployeesForeignKeysViewModel.EmploymentStatusTypes, "", new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.EmploymentStatusTypeId, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Birthdate, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Birthdate, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Birthdate, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.IsActive, new { @class = "control-label col-md-2" }) <div class="col-md-10"> <div class="checkbox"> @Html.EditorFor(model => model.IsActive) </div> @Html.ValidationMessageFor(model => model.IsActive, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
Delete.cshtml – To delete an Entity in the database
@model Employees.Web.Controllers.ViewModels.Employees.EmployeesViewModel @{ ViewBag.Title = "Details"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>Delete</h2> <h3>Are you sure you want to delete this?</h3> <div> <h4>Employee</h4> <hr /> <dl class="dl-horizontal"> <dt> @Html.DisplayNameFor(model => model.FirstName) </dt> <dd> @Html.DisplayFor(model => model.FirstName) </dd> <dt> @Html.DisplayNameFor(model => model.LastName) </dt> <dd> @Html.DisplayFor(model => model.LastName) </dd> <dt> @Html.DisplayNameFor(model => model.DepartmentId) </dt> <dd> @Html.DisplayFor(model => model.DepartmentId) </dd> <dt> @Html.DisplayNameFor(model => model.PositionId) </dt> <dd> @Html.DisplayFor(model => model.PositionId) </dd> <dt> @Html.DisplayNameFor(model => model.EmploymentStatusTypeId) </dt> <dd> @Html.DisplayFor(model => model.EmploymentStatusTypeId) </dd> <dt> @Html.DisplayNameFor(model => model.Birthdate) </dt> <dd> @Html.DisplayFor(model => model.Birthdate) </dd> <dt> @Html.DisplayNameFor(model => model.IsActive) </dt> <dd> @Html.DisplayFor(model => model.IsActive) </dd> </dl> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-actions no-color"> <input type="submit" value="Delete" class="btn btn-default" /> | @Html.ActionLink("Back to List", "Index") </div> } </div>
Details.cshtml – To view details of an entity
@model Employees.Web.Controllers.ViewModels.Employees.EmployeesViewModel @{ ViewBag.Title = "Details"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>Details</h2> <div> <h4>Employee</h4> <hr /> <dl class="dl-horizontal"> <dt> @Html.DisplayNameFor(model => model.FirstName) </dt> <dd> @Html.DisplayFor(model => model.FirstName) </dd> <dt> @Html.DisplayNameFor(model => model.LastName) </dt> <dd> @Html.DisplayFor(model => model.LastName) </dd> <dt> @Html.DisplayNameFor(model => model.DepartmentId) </dt> <dd> @Html.DisplayFor(model => model.DepartmentId) </dd> <dt> @Html.DisplayNameFor(model => model.PositionId) </dt> <dd> @Html.DisplayFor(model => model.PositionId) </dd> <dt> @Html.DisplayNameFor(model => model.EmploymentStatusTypeId) </dt> <dd> @Html.DisplayFor(model => model.EmploymentStatusTypeId) </dd> <dt> @Html.DisplayNameFor(model => model.Birthdate) </dt> <dd> @Html.DisplayFor(model => model.Birthdate) </dd> <dt> @Html.DisplayNameFor(model => model.IsActive) </dt> <dd> @Html.DisplayFor(model => model.IsActive) </dd> </dl> </div> <p> @Html.ActionLink("Edit", "Edit", new { id = Model.EmployeeId }) | @Html.ActionLink("Back to List", "Index") </p>
Edit.cshtml – To Edit an Entity
@model Employees.Web.Controllers.ViewModels.Employees.EmployeesViewModel @{ ViewBag.Title = "Edit"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>Edit</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>Employee</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) @Html.HiddenFor(model => model.EmployeeId) <div class="form-group"> @Html.LabelFor(model => model.FirstName, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.LastName, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.DepartmentId, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.DepartmentId, Model.EmployeesForeignKeysViewModel.Departments, "", new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.DepartmentId, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.PositionId, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.PositionId, Model.EmployeesForeignKeysViewModel.Positions, "", new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.PositionId, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.EmploymentStatusTypeId, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.DropDownListFor(model => model.EmploymentStatusTypeId, Model.EmployeesForeignKeysViewModel.EmploymentStatusTypes, "", new { @class = "form-control" }) @Html.ValidationMessageFor(model => model.EmploymentStatusTypeId, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Birthdate, new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Birthdate, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Birthdate, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.IsActive, new { @class = "control-label col-md-2" }) <div class="col-md-10"> <div class="checkbox"> @Html.EditorFor(model => model.IsActive) </div> @Html.ValidationMessageFor(model => model.IsActive, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Save" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }
Index.cshtml – To view all Entities on a Table
@model Employees.Web.Controllers.ViewModels.Employees.EmployeesGridViewModel @{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table class="table"> <tr> <th> @Html.DisplayNameFor(model => model.Employees.First().EmployeeId) </th> <th> @Html.DisplayNameFor(model => model.Employees.First().FirstName) </th> <th> @Html.DisplayNameFor(model => model.Employees.First().LastName) </th> <th> @Html.DisplayNameFor(model => model.Employees.First().DepartmentId) </th> <th> @Html.DisplayNameFor(model => model.Employees.First().PositionId) </th> <th> @Html.DisplayNameFor(model => model.Employees.First().EmploymentStatusTypeId) </th> <th> @Html.DisplayNameFor(model => model.Employees.First().Birthdate) </th> <th> @Html.DisplayNameFor(model => model.Employees.First().IsActive) </th> <th></th> </tr> @foreach (var item in Model.Employees) { <tr> <td> @Html.DisplayFor(modelItem => item.EmployeeId) </td> <td> @Html.DisplayFor(modelItem => item.FirstName) </td> <td> @Html.DisplayFor(modelItem => item.LastName) </td> <td> @Html.DisplayFor(modelItem => item.DepartmentId) </td> <td> @Html.DisplayFor(modelItem => item.PositionId) </td> <td> @Html.DisplayFor(modelItem => item.EmploymentStatusTypeId) </td> <td> @Html.DisplayFor(modelItem => item.Birthdate) </td> <td> @Html.DisplayFor(modelItem => item.IsActive) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.EmployeeId }) | @Html.ActionLink("Details", "Details", new { id = item.EmployeeId }) | @Html.ActionLink("Delete", "Delete", new { id = item.EmployeeId }) </td> </tr> } </table>
Then the Dependency Injection items, AutoFac is used in this instance.
Bootstrapper.cs – The main class to inject dependencies for your Controller, Query, Repository and Tasks.
using System.Data.Entity; using System.Web.Mvc; using Autofac; using Autofac.Integration.Mvc; using Employees.Domain; namespace Employees.Web.DependencyInjection { public class Bootstrapper { public static ContainerBuilder Builder; public static void Initialise() { Builder = new ContainerBuilder(); ControllerInstaller.Install(); QueryInstaller.Install(); RepositoryInstaller.Install(); TaskInstaller.Install(); var container = Builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); } } }
ControllerInstaller.cs – Registers your Controllers.
using System.Reflection; using Autofac.Integration.Mvc; namespace Employees.Web.DependencyInjection { public class ControllerInstaller { public static void Install() { Bootstrapper.Builder.RegisterControllers(Assembly.GetExecutingAssembly()); } } }
QueryInstaller.cs – Registers Assembly Types in your Web Project that ends in Query.
using System.Reflection; using Autofac; namespace Employees.Web.DependencyInjection { public class QueryInstaller { public static void Install() { Bootstrapper.Builder.RegisterAssemblyTypes(Assembly.Load("Employees.Web")) .Where(t => t.Name.EndsWith("Query")) .AsImplementedInterfaces() .InstancePerLifetimeScope(); } } }
RepositoryInstaller.cs – Registers all your Generic Types in your Tasks Layer, this also registers the Repository Assembly types in your Infrastructure Layer.
using System.Reflection; using Autofac; using Employees.Infrastructure.Repositories; using Employees.Infrastructure.Repositories.Interfaces; namespace Employees.Web.DependencyInjection { public class RepositoryInstaller { public static void Install() { //Register Generic Repositories Bootstrapper.Builder.RegisterType<UnitOfWork>() .As<IUnitOfWork>() .InstancePerLifetimeScope(); Bootstrapper.Builder.RegisterType<DatabaseFactory>() .As<IDatabaseFactory>() .InstancePerLifetimeScope(); Bootstrapper.Builder.RegisterAssemblyTypes(Assembly.Load("Employees.Infrastructure")) .Where(t => t.Name.EndsWith("Repository")) .AsImplementedInterfaces() .InstancePerLifetimeScope(); } } }
TaskInstaller.cs – Registers your Command Processors, Command Handlers and Validation Handlers.
using System.Reflection; using Autofac; using Employees.Tasks.Base; using Employees.Tasks.Base.Interface; namespace Employees.Web.DependencyInjection { public class TaskInstaller { public static void Install() { var services = Assembly.Load("Employees.Tasks"); Bootstrapper.Builder.RegisterType<CommandProcessor>() .As<ICommandProcessor>() .InstancePerLifetimeScope(); Bootstrapper.Builder.RegisterAssemblyTypes(services) .AsClosedTypesOf(typeof(ICommandHandler<>)) .InstancePerLifetimeScope(); Bootstrapper.Builder.RegisterAssemblyTypes(services) .AsClosedTypesOf(typeof(IValidationHandler<>)) .InstancePerLifetimeScope(); } } }
Usage
Now to use this applicaiton, you need to use Entity Framework with my preferred architecture setup as discussed here (http://www.macaalay.com/2015/10/20/creating-a-solid-architectural-foundation-from-scratch-is-not-really-that-hard/).
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 Employees, Departments, Positions and EmploymentStatusTypes like the one above.
Run the application and define your parameters, such as connection string, your project namespace and what objects to generate. Please take note if you have an existing class with the same name it would overwrite it.
Now it will ask you in what folder you want to save those files, choose your project folder root.
Once finished the application will let you know it has completed
Now open your project and show all the files, you will now see all of the generated files by Effinator, all of it will appear in the Domain, Insfrastructure, Framework and Task Layers
Now include all of them in your project
Don’t forget to get the nuget packages needed, the only things you need to use in this project are latest EntityFramework, the latest ASP.Net MVC, Autofac and Autofac ASP.Net MVC 5 Integration
Then call your IoC bootstrapper in Global.asax like such
Run the project then you will see a whole CRUD process.
List items
Create
Edit
Delete
Downloads
Currently On Beta version:
Source Code : https://sourceforge.net/projects/effinator
Executable : Effinator-Installer.zip