It's been awhile since I have been playing with DbContext. I have been using a Serivce / Repository / UnitOfWork pattern for a number of years now, which has enabled me to use entity framework without worrying about how to add / update and delete.
Now I have decided that for some projects using that design pattern is not suitable (mainly when working with lookup tables). Because of this I have gone back to basics.
Using the DbContext has been find for getting information, but now I have actually started creating my methods for inserting / updating data I seem to have found an issue.
I am fairly certain that the issue is something simple, but I need some help :)
Here is my method:
[HttpPost]
[Route("")]
public IHttpActionResult Import(IList<CollectionBindingModel> models)
{
// If our ModelState is invalid, return a bad request
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
// Loop through our models
foreach (var model in models)
{
// Create our collection
var collection = new Collection()
{
CenterId = model.CenterId,
Reference = model.Reference,
CustomerReference = model.CustomerReference,
CustomerName = model.CustomerName,
CustomerBusinessName = model.CustomerBusinessName,
SupplierName = model.SupplierName,
CollectionCode = model.CollectionCode,
Status = model.Status,
CollectionDate = model.CollectionDate
};
// Add to our database context
this.DbContext.Collections.Add(collection);
}
// Save our changes
this.DbContext.SaveChanges();
}
catch (Exception ex)
{
}
// Return Ok
return Ok();
}
I have merely added the try / catch block for testing purposes.
When I call this method from my interface, it returns OK and throws no errors, but if I check the database I see there have been no inserts.
Can anyone tell me why?
Update 1
So, digging a little deeper. I have a base controller which looks like this:
/// <summary>
/// Handles the creation of universal properties and methods
/// </summary>
public class BaseController : ApiController
{
// Create our public properties
protected DatabaseContext DbContext { get { return new DatabaseContext(); } }
protected UserService UserService { get { return Request.GetOwinContext().GetUserManager<UserService>(); } }
protected RoleService RoleService { get { return Request.GetOwinContext().GetUserManager<RoleService>(); } }
protected ModelFactory ModelFactory { get { return new ModelFactory(this.Request, this.UserService); } }
/// <summary>
/// Used to return the correct error from an Identity Result
/// </summary>
/// <param name="result">The Identity Result to process</param>
/// <returns></returns>
protected IHttpActionResult GetErrorResult(IdentityResult result)
{
// If there is no result, return an internal server error
if (result == null)
return InternalServerError();
// If we have an error
if (!result.Succeeded)
{
// If we have some errors
if (result.Errors != null)
{
// For each error, add to the ModelState
foreach (var error in result.Errors)
ModelState.AddModelError("", error);
}
// If our ModelState is valid
if (ModelState.IsValid)
{
// No ModelState errors are available to send, so just return an empty BadRequest.
return BadRequest();
}
// Return a BadRequest with our ModelState
return BadRequest(ModelState);
}
// Return null if no errors are found
return null;
}
}
Which is where the DbContext is pulled from.
If I change my controller method to this:
[HttpPost]
[Route("")]
public IHttpActionResult Import(IList<CollectionBindingModel> models)
{
// If our ModelState is invalid, return a bad request
if (!ModelState.IsValid)
return BadRequest(ModelState);
try
{
using (var context = new DatabaseContext())
{
// Loop through our models
foreach (var model in models)
{
// Create our collection
var collection = new Collection()
{
CenterId = model.CenterId,
Reference = model.Reference,
CustomerReference = model.CustomerReference,
CustomerName = model.CustomerName,
CustomerBusinessName = model.CustomerBusinessName,
SupplierName = model.SupplierName,
CollectionCode = model.CollectionCode,
Status = model.Status,
CollectionDate = model.CollectionDate
};
// Add to our database context
context.Collections.Add(collection);
}
// Save our changes
context.SaveChanges();
}
}
catch (Exception ex)
{
return InternalServerError(ex);
}
// Return Ok
return Ok();
}
then everything saves OK.
You are newing up a new DbContect everytime you access your DbContext property.
Every time you access this.DbContext you get a new instance of the DbContext class because of the way you have set up your property...
protected DatabaseContext DbContext { get { return new DatabaseContext(); } }
So this line gets a new db context and adds the collection object... on the next itteration of this loop you will get ANOTHER new instance of DbContext and add the next one... you are NOT adding your collection classes to the same object instance...
// Add to our database context
this.DbContext.Collections.Add(collection);
And so does this...
// Save our changes
this.DbContext.SaveChanges();
That means you are basically calling save changes on a brand new instance of DbContext that has absolutely no changes. You should always add and attach your entities to the same instance of DbContext that you then call save changes on.
You have fixed the issue in your new code because you create a new DbContext instance in the constructor of your base class and then use that same instance throughout...
// Create our public properties
protected DatabaseContext DbContext { get; private set; }
protected UserService UserService { get { return Request.GetOwinContext().GetUserManager<UserService>(); } }
protected RoleService RoleService { get { return Request.GetOwinContext().GetUserManager<RoleService>(); } }
protected ModelFactory ModelFactory { get { return new ModelFactory(this.Request, this.UserService); } }
public BaseController()
{
this.DbContext = new DatabaseContext();
}
This would also work, when you access the property getter it will return the same instance if it has been initialised... or initialise it first and then return it...
private DatabaseContext _dbContext;
protected DatabaseContext DbContext
{
get { return this._dbContext ?? (this._dbContext = new DatabaseContext()); }
}
Ok, I have fixed this but I have no idea why.
If I change my BaseController to this:
/// <summary>
/// Handles the creation of universal properties and methods
/// </summary>
public class BaseController : ApiController
{
// Create our public properties
protected DatabaseContext DbContext { get; private set; }
protected UserService UserService { get { return Request.GetOwinContext().GetUserManager<UserService>(); } }
protected RoleService RoleService { get { return Request.GetOwinContext().GetUserManager<RoleService>(); } }
protected ModelFactory ModelFactory { get { return new ModelFactory(this.Request, this.UserService); } }
public BaseController()
{
this.DbContext = new DatabaseContext();
}
/// <summary>
/// Used to return the correct error from an Identity Result
/// </summary>
/// <param name="result">The Identity Result to process</param>
/// <returns></returns>
protected IHttpActionResult GetErrorResult(IdentityResult result)
{
// If there is no result, return an internal server error
if (result == null)
return InternalServerError();
// If we have an error
if (!result.Succeeded)
{
// If we have some errors
if (result.Errors != null)
{
// For each error, add to the ModelState
foreach (var error in result.Errors)
ModelState.AddModelError("", error);
}
// If our ModelState is valid
if (ModelState.IsValid)
{
// No ModelState errors are available to send, so just return an empty BadRequest.
return BadRequest();
}
// Return a BadRequest with our ModelState
return BadRequest(ModelState);
}
// Return null if no errors are found
return null;
}
}
then everything works.
Note, that the only thing I have changed is moving the setting of the dbcontext to the constructor.
I suspect that it is because the way it was before, created a new database context every time someone requested it. This time it sets it on creation of the base controller.
Related
I'm attempting to write a unit test for my repository class for its Create method that uses DbContext and the Add method.
My idea was to get a count of the number of existing records. Call the Create method. Get the new count of records and check that its incremented by 1.
However, when running the unit test, it errors when calling the Add method with the following error:
{"Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\r\nObject name: 'DbContext'."}
I'm trying to understand why is this and how to overcome this?
public class MyDatabaseContext : DbContext
{
public MyDatabaseContext(DbContextOptions<MyDatabaseContext> options) : base(options)
{
}
public DbSet<Record> Records { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Record>();
}
}
public class Repository : IRepository
{
private readonly MyDatabaseContext _dbContext;
public Repository(MyDatabaseContext dbContext)
{
_dbContext = dbContext;
}
public Record Create(Record record)
{
try
{
using (_dbContext)
{
var response = _dbContext.Records.Add(record); //erroring line
_dbContext.SaveChanges();
return response.Entity;
}
}
catch (Exception ex)
{
return null;
}
}
public IEnumerable<Record> GetAll()
{
try
{
using (_dbContext)
{
return _dbContext.Records.ToList();
}
}
catch (Exception ex)
{
return null;
}
}
}
public interface IRepository
{
Record Create(Record record);
IEnumerable<Record> GetAll();
}
Startup.cs:
services.AddDbContext<MyDatabaseContext>(opt => opt.UseInMemoryDatabase("memoryDb"));
services.AddScoped<IRepository, Repository>();
Unit test:
[TestMethod]
public async Task Create_Successfully()
{
var repository = new Repository(await GetDbContext());
var existingRecords = repository.GetAll();
repository.Create(new Record());
var newRecords = repository.GetAll();
Assert.AreEqual(3, existingRecords.Count());
Assert.AreEqual(4, newRecords.Count());
}
private async Task<DbContext> GetDbContext()
{
var options = new DbContextOptionsBuilder<DbContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;
var context = new DbContext(options);
context.Database.EnsureCreated();
if (await context.Records.CountAsync() <= 0)
{
for (int i = 1; i <= 3; i++)
{
context.Records.Add(new Records());
await context.SaveChangesAsync();
}
}
return context;
}
You need to remove the using statements in the GetAll and Create methods:
public Record Create(Record record)
{
try
{
using (_dbContext)
{
var response = _dbContext.Records.Add(record); //erroring line
_dbContext.SaveChanges();
return response.Entity;
}
}
catch (Exception ex)
{
return null;
}
}
To:
public Record Create(Record record)
{
try
{
var response = _dbContext.Records.Add(record); //erroring line
_dbContext.SaveChanges();
return response.Entity;
}
catch (Exception ex)
{
return null;
}
}
You don't need to worry about disposing the service as the conatiner will do that for you in production. In your tests you can do this if you want to clean things up:
[TestMethod]
public async Task Create_Successfully()
{
using (var context = await GetDbContext())
{
var repository = new Repository(context);
var existingRecords = repository.GetAll();
repository.Create(new Record());
var newRecords = repository.GetAll();
Assert.AreEqual(3, existingRecords.Count());
Assert.AreEqual(4, newRecords.Count());
}
}
I'm working on moving my API logic in my PATCH endpoint to a Mediatr Command. When applying my patch document, I usually check the model state like below. Normally, I'm doing this from a controller so there is no issue, but when moving this into a RequestHandler, I no longer have access to the model state property since I'm outside of the controller.
How would you recommend going about this?
Here is the model state logic I'd like to use outside of the controller:
updatePartialValueToReplaceCommand.PatchDoc.ApplyTo(valueToReplaceToPatch, ModelState); // apply patchdoc updates to the updatable valueToReplace
if (!TryValidateModel(valueToReplaceToPatch))
{
return ValidationProblem(ModelState);
}
The rest of the code for context:
Patch Endpoint
[HttpPatch("{valueToReplaceId}")]
public IActionResult PartiallyUpdateValueToReplace(int valueToReplaceId, JsonPatchDocument<ValueToReplaceForUpdateDto> patchDoc)
{
var query = new UpdatePartialValueToReplaceCommand(valueToReplaceId, patchDoc);
var result = _mediator.Send(query);
switch (result.Result.ToUpper())
{
case "NOTFOUND":
return NotFound();
case "NOCONTENT":
return NoContent();
default:
return BadRequest();
}
}
UpdatePartialValueToReplaceCommand
public class UpdatePartialValueToReplaceCommand : IRequest<string>
{
public int ValueToReplaceId { get; set; }
public JsonPatchDocument<ValueToReplaceForUpdateDto> PatchDoc { get; set; }
public UpdatePartialValueToReplaceCommand(int valueToReplaceId, JsonPatchDocument<ValueToReplaceForUpdateDto> patchDoc)
{
ValueToReplaceId = valueToReplaceId;
PatchDoc = patchDoc;
}
}
(BROKEN) UpdatePartialValueToReplaceHandler
public class UpdatePartialValueToReplaceHandler : IRequestHandler<UpdatePartialValueToReplaceCommand, string>
{
private readonly IValueToReplaceRepository _valueToReplaceRepository;
private readonly IMapper _mapper;
public UpdatePartialValueToReplaceHandler(IValueToReplaceRepository valueToReplaceRepository
, IMapper mapper)
{
_valueToReplaceRepository = valueToReplaceRepository ??
throw new ArgumentNullException(nameof(valueToReplaceRepository));
_mapper = mapper ??
throw new ArgumentNullException(nameof(mapper));
}
public async Task<string> Handle(UpdatePartialValueToReplaceCommand updatePartialValueToReplaceCommand, CancellationToken cancellationToken)
{
if (updatePartialValueToReplaceCommand.PatchDoc == null)
{
return "BadRequest";
}
var existingValueToReplace = _valueToReplaceRepository.GetValueToReplace(updatePartialValueToReplaceCommand.ValueToReplaceId);
if (existingValueToReplace == null)
{
return "NotFound";
}
var valueToReplaceToPatch = _mapper.Map<ValueToReplaceForUpdateDto>(existingValueToReplace); // map the valueToReplace we got from the database to an updatable valueToReplace model
updatePartialValueToReplaceCommand.PatchDoc.ApplyTo(valueToReplaceToPatch, ModelState); // apply patchdoc updates to the updatable valueToReplace -- THIS DOESN'T WORK IN A MEDIATR COMMAND BECAUSE I DON'T HAVE CONTROLLERBASE CONTEXT
if (!TryValidateModel(valueToReplaceToPatch))
{
return ValidationProblem(ModelState);
}
_mapper.Map(valueToReplaceToPatch, existingValueToReplace); // apply updates from the updatable valueToReplace to the db entity so we can apply the updates to the database
_valueToReplaceRepository.UpdateValueToReplace(existingValueToReplace); // apply business updates to data if needed
_valueToReplaceRepository.Save(); // save changes in the database
return "NoContent";
}
}
If your command handler is dependent on more information than it received in the command in order to act on it, then you're not providing enough information in the command; in this case, if you need to apply an operation based on ModelState, then you'd need to include and pass ModelState in the command.
Whether you can effectively do that or not, I'd more deeply call into question the need to use MediatR or some form of command bus here in the first place; you're synchronously performing an operation and waiting on the response, plus the handler is attempting to perform a lot of behavior (repository get, model validation, repository save), so although you've reduced the amount of code in the controller, you've really only shifted it to a new place that is still tightly coupled and now just obfuscates the dependencies of the controller.
The behavior you've packed into the Controller -> Command -> Handler and back seems like it would be just as well served by some form of provider (or likely multiple providers) injected into your controller via dependency injection; you can use an interface to keep your code flexible and your dependencies obvious while still reducing the grunt work being done within the controller code itself to help keep it clean by instead calling the (still abstracted) descriptive methods that express the intent.
Update 1
This is not a fully conceptualized example, but hopefully illustrative. If you wanted to use the command bus for cross-cutting concerns, there's still room to do so, but only after you've performed input validation, etc. No need to pass any of the controller state anymore.
public class YourController : Controller
{
private readonly ILogger<YourController> _logger;
private readonly IModelPatcher<SomeInput, SomeOutput> _modelPatcher;
private readonly IWriteRepository<SomeOutput> _writeRepository;
public YourController(ILogger<YourController> logger, IModelPatcher<SomeInput, SomeOutput> modelPatcher, IWriteRepository<SomeOutput> writeRepository)
{
_logger = logger;
_modelPatcher = modelPatcher;
_writeRepository = writeRepository;
}
[HttpPatch("{valueToReplaceId}")]
public IActionResult PartiallyUpdateValueToReplace(int valueToReplaceId, JsonPatchDocument<SomeInput> patchDoc)
{
if (patchDoc == null) return BadRequest();
var result = _modelPatcher.ApplyPatch(patchDoc, valueToReplaceId);
if (result == null) return NotFound();
if (!TryValidateModel(result)) return ValidationProblem(ModelState);
// var mapToDto = _mapper.Map(result); // maybe even here, before the repo...
_writeRepository.Update(result); // <-- This could be a command! Model is ready, validation is done.
return NoContent();
}
}
public class SomeInput { }
public class SomeOutput { }
public interface IModelPatcher<in TInput, out TResult>
{
TResult ApplyPatch(JsonPatchDocument<TInput> inputModel, int value);
}
public class SomeInputModelPatcher : IModelPatcher<SomeInput, SomeOutput>
{
private readonly IReadRepository<Something> _repository;
public SomeInputModelPatcher(IReadRepository<Something> repository)
{
_repository = repository;
}
public SomeOutput ApplyPatch(JsonPatchDocument<SomeInput> inputModel, int value)
{
// Do the patch related work
return new SomeOutput();
}
}
For those that are interested, here's what I ended up doing. Also got rid of those annoying magic strings!
[HttpPatch("{valueToReplaceId}")]
public IActionResult PartiallyUpdateValueToReplace(int valueToReplaceId, JsonPatchDocument<ValueToReplaceForUpdateDto> patchDoc)
{
var query = new UpdatePartialValueToReplaceCommand(valueToReplaceId, patchDoc, this);
var result = _mediator.Send(query);
return result.Result;
}
public class UpdatePartialValueToReplaceCommand : IRequest<IActionResult>
{
public int ValueToReplaceId { get; set; }
public JsonPatchDocument<ValueToReplaceForUpdateDto> PatchDoc { get; set; }
public Controller Controller { get; set; }
public UpdatePartialValueToReplaceCommand(int valueToReplaceId, JsonPatchDocument<ValueToReplaceForUpdateDto> patchDoc,
Controller controller)
{
ValueToReplaceId = valueToReplaceId;
PatchDoc = patchDoc;
Controller = controller;
}
}
public class UpdatePartialValueToReplaceHandler : IRequestHandler<UpdatePartialValueToReplaceCommand, IActionResult>
{
private readonly IValueToReplaceRepository _valueToReplaceRepository;
private readonly IMapper _mapper;
public UpdatePartialValueToReplaceHandler(IValueToReplaceRepository valueToReplaceRepository
, IMapper mapper)
{
_valueToReplaceRepository = valueToReplaceRepository ??
throw new ArgumentNullException(nameof(valueToReplaceRepository));
_mapper = mapper ??
throw new ArgumentNullException(nameof(mapper));
}
public async Task<IActionResult> Handle(UpdatePartialValueToReplaceCommand updatePartialValueToReplaceCommand, CancellationToken cancellationToken)
{
if (updatePartialValueToReplaceCommand.PatchDoc == null)
{
return updatePartialValueToReplaceCommand.Controller.BadRequest();
}
var existingValueToReplace = _valueToReplaceRepository.GetValueToReplace(updatePartialValueToReplaceCommand.ValueToReplaceId);
if (existingValueToReplace == null)
{
return updatePartialValueToReplaceCommand.Controller.NotFound();
}
var valueToReplaceToPatch = _mapper.Map<ValueToReplaceForUpdateDto>(existingValueToReplace); // map the valueToReplace we got from the database to an updatable valueToReplace model
updatePartialValueToReplaceCommand.PatchDoc.ApplyTo(valueToReplaceToPatch, updatePartialValueToReplaceCommand.Controller.ModelState); // apply patchdoc updates to the updatable valueToReplace
if (!updatePartialValueToReplaceCommand.Controller.TryValidateModel(valueToReplaceToPatch))
{
return updatePartialValueToReplaceCommand.Controller.ValidationProblem(updatePartialValueToReplaceCommand.Controller.ModelState);
}
_mapper.Map(valueToReplaceToPatch, existingValueToReplace); // apply updates from the updatable valueToReplace to the db entity so we can apply the updates to the database
_valueToReplaceRepository.UpdateValueToReplace(existingValueToReplace); // apply business updates to data if needed
_valueToReplaceRepository.Save(); // save changes in the database
return updatePartialValueToReplaceCommand.Controller.NoContent();
}
}
In my first asp.net mvc application I'm handling errors with try-catch blocks and returning specific messages to the user as Httpstatuscode.
In every crud operation there is same code block.
I tried using exceptionhandler attribute but I couldn't manage to return status code or custom message with it.
Is there any way to replace these try catch blocks on every function and return a message to user?
This is what I tried :
public class ExceptionHandlerFilterAttribute : FilterAttribute, IExceptionFilter
{
private ILogger _logger;
public void OnException(ExceptionContext filterContext)
{
_logger = new NLogLogger();
if (!filterContext.ExceptionHandled)
{
var controller = filterContext.RouteData.Values["controller"].ToString();
var action = filterContext.RouteData.Values["action"].ToString();
var message = filterContext.Exception;
_logger.Log(Business.Enums.LogLevel.Error, string.Concat("/",controller,"/",action), message);
filterContext.ExceptionHandled = true;
filterContext.Result = new ViewResult()
{
ViewName = "Error"
};
}
}
}
This is an example method :
public HttpStatusCodeResult Create(Product product)
{
if (!ModelState.IsValid) return new HttpStatusCodeResult(HttpStatusCode.BadGateway);
try
{
_productService.Create(product);
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
catch (Exception) { return new HttpStatusCodeResult(HttpStatusCode.InternalServerError); }
}
I would like to replace repetitive try-catch blocks for a better code.
You can wrap your methods with something like this:
/// <summary>
/// Tries the specified action.
/// </summary>
/// <param name="action">The action.</param>
public static HttpStatusCodeResult Try(Action action, ModelState model)
{
if (!model.IsValid) return new HttpStatusCodeResult(HttpStatusCode.BadGateway);
try
{
action();
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
catch (Exception) { return new HttpStatusCodeResult(HttpStatusCode.InternalServerError); }
}
And you can use your Try:
public HttpStatusCodeResult Create(Product product)
{
return Try(()=> {
_productService.Create(product);
}, ModelState);
}
Here an wrapper example in github
And the call of that try
Try to set HttpStatusCodeResult for Result property of filterContext:
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.InternalServerError);
I am running a unit test of my PostMyModel route. However, within PostMyModel() I used the line Validate<MyModel>(model) to revalidate my model after it is changed. I am using a test context, so as not to be dependent on the db for the unit tests. I have posted the test context and post method below:
Test Context
class TestAppContext : APIContextInterface
{
public DbSet<MyModel> MyModel { get; set; }
public TestAppContext()
{
this.MyModels = new TestMyModelDbSet();
}
public int SaveChanges(){
return 0;
}
public void MarkAsModified(Object item) {
}
public void Dispose() { }
}
Post Method
[Route(""), ResponseType(typeof(MyModel))]
public IHttpActionResult PostMyModel(MyModel model)
{
//Save model in DB
model.status = "Waiting";
ModelState.Clear();
Validate<MyModel>(model);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.MyModels.Add(model);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (MyModelExists(model.id))
{
return Conflict();
}
else
{
throw;
}
}
return CreatedAtRoute("DisplayMyModel", new { id = model.id }, model);
}
When the Validate<MyModel>(model) line runs, I get the error :
System.InvalidOperationException: ApiController.Configuration must not be null.
How can I correct this?
In order for the Validate command to run, there must be mock HttpRequest associated with the controller. The code to do this is below. This will mock a default HttpRequest, which is fairly unused in this case, allowing the method to be unit tested.
HttpConfiguration configuration = new HttpConfiguration();
HttpRequestMessage request = new HttpRequestMessage();
controller.Request = request;
controller.Request.Properties["MS_HttpConfiguration"] = configuration;
Hi I have table employee with some fields
to validate fields I have created two layers
Service layer
Employee repository
Employee repository code is
namespace MvcApplication2.Models
{
public interface IEmployeeMainTableRepository
{
bool CreateEmployee(EMP_MAIN_TBL EmployeeToCreate);
IEnumerable<EMP_MAIN_TBL> ListEmployees();
}
public class EmployeeRepository : MvcApplication2.Models.IEmployeeMainTableRepository
{
private EMPLOYEE_SYSTEMEntities _entities = new EMPLOYEE_SYSTEMEntities();
public IEnumerable<EMP_MAIN_TBL> ListEmployees()
{
return _entities.EMP_MAIN_TBL.ToList();
}
public bool CreateEmployee(EMP_MAIN_TBL EmployeeToCreate)
{
try
{
// _entities.AddToEMP_MAIN_TBL(productToCreate);
_entities.SaveChanges();
return true;
}
catch
{
return false;
}
}
}
And service layer contains
public interface IEmployeeService
{
bool CreateEmployee(EMP_MAIN_TBL EmployeeToCreate);
System.Collections.Generic.IEnumerable<EMP_MAIN_TBL> ListEmployees();
}
public class EmployeeService : MvcApplication2.Models.IEmployeeService
{
private IValidationDictionary _validatonDictionary;
private IEmployeeMainTableRepository _repository;
public EmployeeService(IValidationDictionary validationDictionary, IEmployeeMainTableRepository repository)
{
_validatonDictionary = validationDictionary;
_repository = repository;
}
protected bool ValidateEmployee(EMP_MAIN_TBL employeeToValidate)
{
if (employeeToValidate.EMP_NM == null)
_validatonDictionary.AddError("EMP_NM", "Name is required.");
if (employeeToValidate.PLCE_OF_BRTH == null)
_validatonDictionary.AddError("PLCE_OF_BRTH", "Place of birth is required.");
return _validatonDictionary.IsValid;
}
public IEnumerable<EMP_MAIN_TBL> ListEmployees()
{
return _repository.ListEmployees();
}
public bool CreateEmployee(EMP_MAIN_TBL EmployeeToCreate)
{
// Validation logic
if (!ValidateEmployee(EmployeeToCreate))
return false;
// Database logic
try
{
_repository.CreateEmployee(EmployeeToCreate);
}
catch
{
return false;
}
return true;
}
and I have created two more classes to add validation messages
public interface IValidationDictionary
{
void AddError(string key, string errorMessage);
bool IsValid { get; }
}
And
public class ModelStateWrapper : IValidationDictionary
{
private ModelStateDictionary _modelState;
public ModelStateWrapper(ModelStateDictionary modelState)
{
_modelState = modelState;
}
#region IValidationDictionary Members
public void AddError(string key, string errorMessage)
{
_modelState.AddModelError(key, errorMessage);
}
public bool IsValid
{
get { return _modelState.IsValid; }
}
#endregion
}
finally employee controllers contains below structure
public class EmployeeController : Controller
{
private IEmployeeService _service;
public EmployeeController()
{
_service = new EmployeeService(new ModelStateWrapper(this.ModelState), new EmployeeRepository());
}
public EmployeeController(IEmployeeService service)
{
_service = service;
}
public ActionResult Index()
{
return View(_service.ListEmployees());
}
//
// GET: /Product/Create
public ActionResult Create()
{
return View(new EMP_MAIN_TBL());
}
//
// POST: /Product/Create
[AcceptVerbs(HttpVerbs.Post)]
[HttpPost]
public ActionResult Create([Bind(Exclude = "EMP_ID")] EMP_MAIN_TBL employeeToCreate)
{
if (!_service.CreateEmployee(employeeToCreate))
return View();
return RedirectToAction("Index");
}
}
}
and my view looks like this
My question is above code working fine for server side validation
but how do I achieve validation on client side using above same code
please
Since you are already validating on the service side you could return the ModelStateDictionary instead of the bool, you could then check that it is valid on the client side.
But this won't help when it comes to checking that the whole service method has finished, so you could create a new type that returns say a bool and the ModelStateDictionary.
Another approach is to use Fault Exceptions. You can create your own fault exception that would get thrown when the model state is not valid. This Model State Fault could contain your ModelStateDictionary.
So from that you have three options.
Change the return type to the ModelStateDictionary.
Create a new return type to return a result and a ModelStateDictionary.
Use Fault Exceptions that occur when the Model State isn't valid.
Personally I would use the third approach, as you can then still use your original return type, and then just need to catch the Fault like you would an Exception. Here is an example and also MSDN