Validations and passing ModelState from Service Layer to Controller WebAPI - c#

I was following the below link to get the validations done at Service Layer
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/models-data/validating-with-a-service-layer-cs
I ended up having cyclic dependency. So remove dependency injection of validation/ModelStateWrapper and used Initialize static way.
Brief Explanation about the Project: I have API, Services Layer, Repository, Database. I am using Automapper to map DTO's to Entities.
Controller:
public StudentController(IStudentService studentService, IMapper mapper)
{
_studentService = studentService;
_studentService.Initialize(new ModelStateWrapper(this.ModelState));
_mapper = mapper;
}
public IActionResult CreateStudent([FromBody] StudentCreationDto student)
{
if (student== null)
{
return BadRequest();
}
if (!ModelState.IsValid)
{
return new UnprocessableEntityObjectResult(ModelState);
}
var studentEnitty = _mapper.Map<Student>(student);
var createSuccessful = _studentService.CreateStudent(studentEnitty);
if (!createSuccessful)
{
return new UnprocessableEntityObjectResult(ModelState);
}
}
Service:
public void Initialize(IValidationDictionary validationDictionary)
{
_validationDictionary = validationDictionary;
}
public bool CreateStudent(Student student)
{
//Check if Student already exists
if (!ValidateStudentToCreate(student))
{
return false;
}
//Create Portal
var createSuccessful = _studentRepository.CreateStudent(student);
if (!createSuccessful)
{
return false;
}
return true;
}
private bool ValidateStudentToCreate(Student student)
{
//throw new Exception();
if (_studentRepository.StudentEmailExists(student.Email))
{
_validationDictionary.AddError("Email", "Student already exists");
}
bool isValid = _validationDictionary.IsValid;
return isValid;
}
I have IValidation dictionary with AddError and IsValid. ModelStateWrapper Implements IValidationDictionary.
IValidation Dictionary
public interface IValidationDictionary
{
void AddError(string key, string errorMessage);
bool IsValid { get; }
}
ModelStateWrapper
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; }
}
I am trying to get the data model validations at Controller level and Business logic such as Student exists at Service level. I am able to execute the validations however, i am unable to get the model state back to Controller as it says valid. Can you please let me know what I am missing?

Related

Get parameter custom mapping

I want to write many GET handlers that receive an ID for an object,
site.com/controller/Action1/1234
site.com/controller/Action2/1234
site.com/controller/Action3/1234
I would like to write the code that fetches the complex object from the DB just once:
class ComplexObject
{
public string str1 { get; set; }
public string str2 { get; set; }
}
ComplexObject GetFromId(string id)
{
ComplexObject x = Database.GetById(id);
if (x == null)
{
return Http404();
}
return x;
}
and then just use the object directly:
[Route("/[controller]/[action]/{message}")]
[HttpGet]
public string Action1(ComplexObject message)
{
return message.str1;
}
[Route("/[controller]/[action]/{message}")]
[HttpGet]
public string Action2(ComplexObject message)
{
return message.str1;
}
[Route("/[controller]/[action]/{message}")]
[HttpGet]
public string Action3(ComplexObject message)
{
return message.str1;
}
And that all of my handlers will just get the object, and won't have to check whether the ID is correct, etc.
How is that possible?
The official Microsoft Docs describe exactly how you can bind route parameters to a complex object from a database using a custom model binder.
Here's their example model binder:
public class AuthorEntityBinder : IModelBinder
{
private readonly AuthorContext _context;
public AuthorEntityBinder(AuthorContext context)
{
_context = context;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var modelName = bindingContext.ModelName;
// Try to fetch the value of the argument by name
var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
if (valueProviderResult == ValueProviderResult.None)
{
return Task.CompletedTask;
}
bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
var value = valueProviderResult.FirstValue;
// Check if the argument value is null or empty
if (string.IsNullOrEmpty(value))
{
return Task.CompletedTask;
}
if (!int.TryParse(value, out var id))
{
// Non-integer arguments result in model state errors
bindingContext.ModelState.TryAddModelError(
modelName, "Author Id must be an integer.");
return Task.CompletedTask;
}
// Model will be null if not found, including for
// out of range id values (0, -3, etc.)
var model = _context.Authors.Find(id);
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
And then there are various ways to use this new model binder. One is to add an attribute on the model itself:
[ModelBinder(BinderType = typeof(AuthorEntityBinder))]
public class Author
{
// snip
}
Another is to use an attribute on the action parameters:
[HttpGet("{id}")]
public IActionResult GetById([ModelBinder(Name = "id")] Author author)
{
// snip
}
I am not sure why one would want to do what you are proposing, but it unnecessarily overcomplicates things and causes dependencies on the model binder.
Here is how I would implement this:
Have a class that manages your complex object and hide it behind an interface, the inject it into the controller:
public interface IComplexObjectManager
{
ComplexObject GetFromId(string id);
}
public class ComplexObjectManager : IComplexObjectManager
{
private readonly Database _database;
public ComplexObjectManager(Database database)
{
_database = database;
}
public ComplexObject GetFromId(string id)
{
ComplexObject x = _database.GetById(id);
return x;
}
}
[ApiController]
public class ComplexObjectController
{
public ComplexObjectController(IComplexObjectManager complexObjectManager)
{
ObjectManager = complexObjectManager;
}
public IComplexObjectManager ObjectManager { get; }
}
Then consume it in your method, changing the return type to an action result:
[Route("/[controller]/[action]/{id}")]
[HttpGet]
public IActionResult Action1(string id)
{
var obj = ObjectManager.GetFromId(id);
if(obj != null)
return Ok(obj.str1);
else
return NotFound();
}
Make sure to handle the response accordingly.
This approach decouples things (further abstraction can be added for Database), and allows for injection and unit testing.
Please check the code for consistency. I wrote this in a hurry.
I'm not doing the exactly thing that you are asking but i think it can help you. First of all, i'm using BaseController for it because you can filter your all actions before they are getting executed.
public class BaseController : Controller
{
#region /*IoC*/
public BaseViewModel baseViewModel;
public IUnitOfWork<Product> unitOfWorkProductForCart;
#endregion
#region /*ctor*/
public BaseController(IUnitOfWork<Product> unitOfWorkProductForCart)
{
this.unitOfWorkProduct = unitOfWorkProduct;
}
#endregion
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string controllerName = filterContext.ActionDescriptor.RouteValues["controller"];
string actionName = filterContext.ActionDescriptor.RouteValues["action"];
if (actionName == "ProductDetails")
{
var urlParameters = filterContext.ActionArguments;
if (urlParameters.Count != 0)
{
var isThatSlug = urlParameters.ElementAt(0).Key;
if (isThatSlug == "slug")
{
var slugCondition = urlParameters.ElementAt(0).Value;
var isThatProductExist = unitOfWorkProduct.RepositoryProduct.GetProductBySlugForChecking(slugCondition.ToString());
if (isThatProductExist.Count == 0)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
{
{"controller","Account"},
{"action","NotFound"}
});
}
}
}
}
}
}
in that example, i'm controlling the parameters. if it's something like i don't want, it's redirects you to the NotFound page.
i hope it can give you a idea

Cannot serialize ISession when using Newtonsoft.Json library

my program is displaying the following error when trying to save some data without saving it to database and displaying it in json.
Newtonsoft.Json.JsonSerializationException: 'Could not create an instance of type Microsoft.AspNetCore.Http.ISession. Type is an interface or abstract class and cannot be instantiated. Path 'Session.IsAvailable', line 1, position 26.'
My Error Pic
This code is written using a repository design template.
The code written in the comment works. Should I change the code GetJson<T> in the new version? after change using Newtonsoft.Json TO using System.Text.Json.Serialization?
public class CartController : Controller
{
private IProductRepository repository;
private Cart _cart;
public CartController(IProductRepository repo, Cart cart)
{
repository = repo;
_cart = cart;
}
public ViewResult Index(string returnurl)
{
ViewBag.ReturnUrl = returnurl;
//return View(GetCart());
return View(_cart);
}
public IActionResult AddToCart(int ProductID,string returnUrl)
{
Product product = repository.GetById(ProductID);
if (product != null)
{
//Cart cart = GetCart();
//cart.AddItem(product, 1);
//SaveCart(cart);
_cart.AddItem(product, 1);
}
return RedirectToAction("Index", new { returnUrl });
}
public IActionResult RemoveFromCart(int ProductID, string returnUrl)
{
Product product = repository.GetById(ProductID);
if (product != null)
{
//Cart cart = GetCart(); //-----> Worked
//cart.RemoveLine(product); //-----> Worked
//SaveCart(cart); //-----> Worked
_cart.RemoveLine(product); //Not Working
}
return RedirectToAction("Index", new { returnUrl });
}
//private Cart GetCart() //-->Worked
//{
// Cart cart = HttpContext.Session.GetJson<Cart>("Cart") ?? new Cart();
// return cart;
//}
//private void SaveCart(Cart cart) //-->Worked
//{
// HttpContext.Session.SetJson("Cart", cart);
//}
}
public static class SessionExtensions
{
public static void SetJson(this ISession session, string key, object value)
{
session.SetString(key, JsonConvert.SerializeObject(value));
}
public static T GetJson<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default(T) :
JsonConvert.DeserializeObject<T>(value); //error exception throw here
}
}
and:
public class SessionCart : Cart
{
public static Cart GetCart(IServiceProvider services)
{
ISession session = services.GetRequiredService<IHttpContextAccessor>()?.HttpContext.Session;
SessionCart cart = session?.GetJson<SessionCart>("Cart") ?? new SessionCart();
cart.Session = session;
return cart;
}
[JsonIgnore]
public ISession Session { get; set; }
public override void AddItem(Product product, int quantity)
{
base.AddItem(product, quantity);
Session.SetJson("Cart", this);
}
public override void RemoveLine(Product product)
{
base.RemoveLine(product);
Session.SetJson("Cart", this);
}
public override void Clear()
{
base.Clear();
Session.Remove("Cart");
}
}
you must remove system.Text.Json.Serialization
and add using Newtonsoft.Json in SessionCart
Just add the following namespace to the SessionCart class.
using Newtonsoft.Json;
I see you are following the Pro ASP.NET Core 2 MVC book by Adam Freeman.
To fix this issue, change the access modifier on the ISession variable from public to private. This will prevent the ISession being serialized and will then deserialize as you would expect.
Before:
[JsonIgnore]
public ISession Session { get; set; }
After:
private ISession session { get; set; }
Then ensure all of your methods can use the lowercase session (to follow naming convention best practice).

Using DI with Entity Framework and multiple data contexts in ASP.NET MVC

I am wondering, how we can handle a situation, and if I'm going about this the wrong way. We wrote a web application based on one ERP's database schema model. We are using Entity Framework for the system, with the regular dependency injection method.
Now that we have purchased multiple ERP's from other labs, we are trying to allow their data be used on our first original web portal built for our original ERP. As you can imagine, this is rough as the database models and design will not line up with ours. For example something like GetAllAssets() stored procedure returns the complex type and then is bound to the repo level, the service level and onto the view.
What I was thinking is that we could maybe add multiple EDMX (context for databases), then keep everything from the Views to the controllers to the service layer the same. At the service layer, and type params to our classes, and so we could pass the context the current user logged in as, and then in auto mapper add new entries for the new database context calls from Entity Framework, to map to our original code from the service back down to the view.
Is this possible, and or a good idea or bad idea?
Here is a example of a current basic controller we are using with DI style, and I have removed a lot of code for this question's example:
[AuthorizeWithSession]
public class LocationController : Controller
{
private readonly IMProAssetLocationService _mProAssetLocationService;
private readonly IUIDataService _uiDataService;
public LocationController(IMProAssetLocationService mProAssetLocationService,
IUIDataService uiDataService)
{
_mProAssetLocationService = mProAssetLocationService;
}
public ActionResult List()
{
return View();
}
public ActionResult List2()
{
return View();
}
public ActionResult GetLocationList([DataSourceRequest]DataSourceRequest request)
{
//var result = DepartmentService.GetDepartmentList(SessionHelper.GetCustId());
var result = _mProAssetLocationService.MProAssetLocationGetLocationByCustID(SessionHelper.GetCustId(), null);
if (result != null && result.Any())
{
return Json(result.ToDataSourceResult(request), JsonRequestBehavior.AllowGet);
}
}
}
The interface service layer:
public interface IMProAssetLocationService
{
IEnumerable<LocationVm> MProAssetLocationGetLocationByCustID(string custId,string id);
string MProAssetLocationInsertLocation(LocationVm vm);
void MProAssetLocationDeleteLocationByCustIDAndLocationID(string custId, string locationId);
void MProAssetLocationUpdateLocationByCustIDAndLocationID(LocationVm vm);
}
The service layer:
public class MProAssetLocationService : LogManager, IMProAssetLocationService
{
private readonly IMProAssetLocationRepo _mProAssetLocationRepo;
public MProAssetLocationService(IMProAssetLocationRepo mProAssetLocationRepo)
{
_mProAssetLocationRepo = mProAssetLocationRepo;
}
protected override Type LogPrefix
{
get { return this.GetType(); }
}
public IEnumerable<LocationVm> MProAssetLocationGetLocationByCustID(string custId, string id)
{
List<LocationVm> listlocationVm = new List<LocationVm>();
try
{
var records = _mProAssetLocationRepo.MProAssetLocationGetLocationByCustID(custId,id);
}
}
The Interface repo layer:
public interface IMProAssetLocationRepo : IRepository<MProAssetLocation>
{
IEnumerable<string> GetMProAssetLocatonByCustId(string custId);
IEnumerable<string> GetMProAssetLocatonDescriptionByCustId(string custId);
IEnumerable<LocationView> GetMProAssetLocatonListByCustId(string search, string locationID, string custId);
IEnumerable<LocationView> MProAssetLocationGetLocationByCustID(string custId, string id);
string MProAssetLocationInsertLocation(LocationView lv);
void MProAssetLocationDeleteLocationByCustIDAndLocationID(string custId, string locationId);
void MProAssetLocationUpdateLocationByCustIDAndLoacationID(LocationView lv);
}
The repo layer:
public class CalLocationsRepo : RepositoryBase<CalLocaton>, ICalLocationsRepo
{
public CalLocationsRepo(IDbFactory dbFactory)
: base(dbFactory)
{
}
//WHERE CalCodeActive=1 AND CalCodeGroup='OSS' ORDER BY CalCode
public IEnumerable<string> GetCalLocations(string empID)
{
return DbContext.TAM_GetCalLocationsList(empID).ToList();
}
}
I was thinking of something like using a context type in our system. I know the entity models calls would be named differently based on our different databases using their own stored procedures, but thought at the level prior to the call i check the context to use, then make the call accordingly like so:
public class DBContextRepo<T> : RepositoryBase, IDBContextRepo<T>
{
DBContextRepo<T> _typeParameterClass;
public DBContextRepo(IDbFactory dbFactory, DBContextRepo<T> typeParameterClass)
: base(dbFactory)
{
_typeParameterClass = typeParameterClass;
}
public List<string> GetAllModelsByManufcaturer(string manufacturerName)
{
List<string> results = new List<string>();
if (_typeParameterClass.GetType() == typeof(TAM.DataLayer.EntityModels.QuoteWerks1Entities))
{
using(var dbContext = DbContextQw)
{
var items = dbContext.Products_OurProducts_Products.Where(p => p.Manufacturer == manufacturerName).ToList();
results = items.Select(p => p.ManufacturerPartNumber).ToList();
}
}
else
{
using (var dbContext = DbContext)
{
var items = dbContext.Models.Where(a => a.Manufacturer.MfrName == manufacturerName);
results = items.Select(m => m.ModelNumber).ToList();
}
}
return results;
}
}
This causes errors and is where im not sure how to handle two DBContext:
public class DbFactory : Disposable, IDbFactory
{
private TAMModel _dbContext;
private QuoteWerks1Entities _dbContextQW;
public TAMModel Init()
{
return _dbContext ?? (_dbContext = new TAMModel());
}
public QuoteWerks1Entities InitQW()
{
return _dbContextQW ?? (_dbContextQW = new QuoteWerks1Entities());
}
protected override void DisposeCore()
{
if (_dbContext != null)
{
_dbContext.Dispose();
}
if (_dbContextQW != null)
{
_dbContextQW.Dispose();
}
}
}
Once i added the second context, all of my regular code say they do not have a corresponding type in constructors such as this one:
public class ContractRepo : RepositoryBase<Contract>, IContractRepo
{
public ContractRepo(IDbFactory dbFactory)
: base(dbFactory)
{
}
public string GetContractIdentifyByCustId(string custId)
{
return DbContext.TAM_GetContractIdentifyByCustId(custId).SingleOrDefault();
}
}
Here is the BaseRepo class:
public class RepositoryBase
{
private readonly TAMModel _dataContext;
private readonly QuoteWerks1Entities _dataContextQW;
protected IDbFactory DbFactory { get; private set; }
protected TAMModel DbContext
{
get
{
return _dataContext ?? DbFactory.Init();
}
}
protected QuoteWerks1Entities DbContextQw
{
get
{
return _dataContextQW ?? DbFactory.InitQW();
}
}
protected RepositoryBase(IDbFactory dbFactory)
{
DbFactory = dbFactory;
}
}
public abstract class RepositoryBase<T> where T : class
{
private readonly TAMModel _dataContext;
private readonly IDbSet<T> _dbSet;
private readonly IDbSet<T> _dbSetQW;
private readonly QuoteWerks1Entities _dataContextQW;
protected IDbFactory DbFactory { get; private set; }
protected TAMModel DbContext
{
get
{
return _dataContext ?? DbFactory.Init();
}
}
protected QuoteWerks1Entities DbContextQW
{
get
{
return _dataContextQW ?? DbFactory.InitQW();
}
}
protected RepositoryBase(IDbFactory dbFactory, T type)
{
DbFactory = dbFactory;
_dbSet = DbContext.Set<T>();
_dbSetQW = DbContextQW.Set<T>();
}
public virtual void Add(T entity)
{
_dbSet.Add(entity);
}
public virtual void Update(T entity)
{
_dbSet.Attach(entity);
DbContext.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);
foreach (T obj in objects)
{
_dbSet.Remove(obj);
}
}
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual T GetById(string 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 Get(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where).SingleOrDefault();
}
public virtual IQueryable<T> Query()
{
return _dbSet;
}
public virtual IQueryable<T> Query(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where);
}
public virtual ObjectQuery<U> CreateQuery<U>(string query, ObjectParameter[] parameters)
{
return CastAsObjectContext().CreateQuery<U>(query, parameters);
}
public virtual ObjectQuery<U> CreateQuery<U>(string query)
{
return CreateQuery<U>(query, new ObjectParameter[0] { });
}
public virtual ObjectQuery<DbDataRecord> CreateQuery(string query, ObjectParameter[] parameters)
{
return CreateQuery<DbDataRecord>(query, parameters);
}
public virtual ObjectQuery<DbDataRecord> CreateQuery(string query)
{
return CreateQuery<DbDataRecord>(query);
}
private ObjectContext CastAsObjectContext()
{
var oContext = (DbContext as IObjectContextAdapter).ObjectContext;
return oContext;
}
}
The only problem is, how do I set the context on a login when using dependency injection? Will this work, as at the moment I'm still in the process, or am I over complicating it and going about it the wrong way? Thanks in advance.

Unity - Resolve implementation of generic interface based on model type

I'm new to Unity and am running into issues when it comes to classes that implement generic interfaces.
Suppose I have one controller and one service class, with the intention that they call the business layer based on an type identifier passed into the controller. Here's my layout:
Models:
public interface IModel
{
string Name { get; }
}
public class MyOrder : IModel
{
public string Name { get { return "Order"; } }
}
public class MyInvoice : IModel
{
public string Name { get { return "Invoice"; } }
}
Business Logic:
public interface ILogic
{
string GetModelName(IModel myModel);
}
public class MyOrderLogic : ILogic
{
public string GetModelName(IModel myModel)
{
return "MyOrderLogic : " + myModel.Name;
}
}
public class MyInvoiceLogic : ILogic
{
public string GetModelName(IModel myModel)
{
return "MyInvoiceLogic : " + myModel.Name;
}
}
public class LogicFactory
{
Func<IModel, ILogic> logicResolver;
public LogicFactory(Func<IModel, ILogic> resolver)
{
logicResolver = resolver;
}
public ILogic GetLogicForModel(IModel model)
{
return logicResolver(model);
}
}
Service:
public class MySingleService
{
private LogicFactory factory;
public MySingleService(LogicFactory f)
{
this.factory = f;
}
public IModel GetModel(int typeId, int objectId)
{
IModel model;
// This would really call Entity Framework context
switch (typeId)
{
case 1:
model = new MyOrder();
break;
default:
model = new MyInvoice();
break;
}
return model;
}
public void DoSomething(IModel model)
{
var logic = factory.GetLogicForModel(model);
var name = logic.GetModelName(model);
Console.WriteLine(name);
}
}
Controller:
public class MyController
{
MySingleService service;
public MyController(MySingleService s)
{
this.service = s;
}
public void DoAnAction(int typeId, int objectId)
{
var model = service.GetModel(typeId, objectId);
service.DoSomething(model);
}
}
This all works pretty well when I configure my Unity container like so
var container = new UnityContainer();
container.RegisterType<ILogic, MyOrderLogic>("MyOrder");
container.RegisterType<ILogic, MyInvoiceLogic>("MyInvoice");
Func<IModel, ILogic> resolver = (myModel) => container.Resolve<ILogic>(myModel.GetType().Name);
var logicFactory = new LogicFactory(resolver);
container.RegisterInstance<LogicFactory>(logicFactory);
var controller = container.Resolve<MyController>();
My issue is that I want to restrict the Business Logic classes so that they can only work on the proper model (i.e. I want MyOrderLogic to only accept MyOrder)
I wanted to have the business layer make use of generics, like so:
// Business Logic
public interface ILogic<T> where T : IModel
{
string GetModelName(T myModel);
}
public class MyOrderLogic : ILogic<MyOrder>
{
public string GetModelName(MyOrder myModel)
{
return "MyOrderLogic : " + myModel.Name;
}
}
public class MyInvoiceLogic : ILogic<MyInvoice>
{
public string GetModelName(MyInvoice myModel)
{
return "MyInvoiceLogic : " + myModel.Name;
}
}
This caused all kinds of problems between the Service and the Controller. I'm not sure how to properly resolve the *Logic classes based on a type identifier. I tried to do something similar to ILogic<IModel> logic = new MyOrderLogic(), but that obviously didn't work.
Is what I'm looking for possible? Is there something I can add to just the LogicFactory or something?
I want to avoid having individual controller and service objects as they would just have duplicate code.

C# Interface Method calls from a controller

I was just working on some application architecture and this may sound like a stupid question but please explain to me how the following works:
Interface:
public interface IMatterDAL
{
IEnumerable<Matter> GetMattersByCode(string input);
IEnumerable<Matter> GetMattersBySearch(string input);
}
Class:
public class MatterDAL : IMatterDAL
{
private readonly Database _db;
public MatterDAL(Database db)
{
_db = db;
LoadAll(); //Private Method
}
public virtual IEnumerable<Matter> GetMattersBySearch(string input)
{
//CODE
return result;
}
public virtual IEnumerable<Matter> GetMattersByCode(string input)
{
//CODE
return results;
}
Controller:
public class MatterController : ApiController
{
private readonly IMatterDAL _publishedData;
public MatterController(IMatterDAL publishedData)
{
_publishedData = publishedData;
}
[ValidateInput(false)]
public JsonResult SearchByCode(string id)
{
var searchText = id; //better name for this
var results = _publishedData.GetMattersBySearch(searchText).Select(
matter =>
new
{
MatterCode = matter.Code,
MatterName = matter.Name,
matter.ClientCode,
matter.ClientName
});
return Json(results);
}
This works, when I call my controller method from jquery and step into it, the call to the _publishedData method, goes into the class MatterDAL.
I want to know how does my controller know to go to the MatterDAL implementation of the Interface IMatterDAL. What if I have another class called MatterDAL2 which is based on the interface. How will my controller know then to call the right method?
I am sorry if this is a stupid question, this is baffling me.
EDIT:
Based on the responses, it seems like this is where the dependency is being resolved:
This is a ninject call:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICpdMatterDAL>().To<CachedCpdData>();
}
Where CachedCpdData is:
public class CachedCpdData : ICpdMatterDAL
{
private static readonly object CacheLockObject = new object();
private readonly MatterDAL _matterData;
public CachedCpdData()
{
_matterData = DomainModel.DataAccessManager.Instance.Matters;
}
public IEnumerable<Matter> GetMattersForAutoCompleteByCode(string input)
{
var cacheKey = string.Format("matter-search-{0}", input ?? "");
var result = HttpRuntime.Cache[cacheKey] as IEnumerable<Matter>;
if (result == null)
{
lock (CacheLockObject)
{
result = HttpRuntime.Cache[cacheKey] as IEnumerable<Matter>;
if (result == null)
{
result = _matterData.GetMattersForAutoCompleteByCode(input).ToList();
HttpRuntime.Cache.Insert(cacheKey, result, null, DateTime.Now.AddSeconds(60), TimeSpan.Zero);
}
}
}
return result;
}
public IEnumerable<Matter> GetMattersByMatterCodeSearch(string input)
{
return _matterData.GetMattersByMatterCodeSearch(input);
}
}
The rason why your code is using the right implementation of IMatterDAL is because it's being passed as a parameter in the constructor of MatterController. I'm almost sure that your code is using some Dependency Injection framework to resolve IMatterDAL.
In fact Ninject is a DI Framework. Your code should have something like
kernel.Bind<IMatterDAL>().To<MatterDAL >();

Categories

Resources