In my application, i have a check, which checks the COUNT of a bunch of database tables. If each of these COUNTS is above a certain threshold, then it sets a property as active. Here is an example of a controller where a user adds room information
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "ID,RoomTypeID,Description")] Room room, int propertyId)
{
if (ModelState.IsValid)
{
room.ID = Guid.NewGuid();
room.DateCreated = DateTime.Now;
room.PropertyID = propertyId;
db.Rooms.Add(room);
await db.SaveChangesAsync();
var roomCount = db.Rooms.Where(r => r.PropertyID == propertyId).Count();
var rateCount = db.Rates.Where(r => r.PropertyID == propertyId).Count();
var imageCount = db.PropertyImage.Where(r => r.PropertyID == propertyId).Count();
if(roomCount >= 3 && rateCount >= 3 && imageCount >= 3)
{
//Set Property as ACTIVE
}
return RedirectToAction("Index");
}
The problem i have, is that i want to run this check (the 3 database COUNTS and the 'if' statement) on a whole bunch of controllers. I don't want to have to duplicate this config on every controller for every action. Also, this check may be susceptible to changing, so i'd like to update it in just one place.
How am i best to go about this? Should i be creating some sort of helper class?
Thanks
You are on the right track. You want to separate that responsibility as much as you can based on your needs, or the levels of separation already established in your application. At a minimum, I would create one class that accesses the DB, and another that contains the logic to make the "if" decision. For instance Controller -> calls Helper -> calls DBAccessor
Related
I have this database call in my controller:
var addresses = db.Addresses.Where(a=>a.EmployeeId == id).ToList();
It works fine the way it is, but I am having a hard time to make it async. When I do make it async, I am able to select all records in the table or a single record, just fine. However, I can't seem to get multiple records with matching EmployeeId.
I found the solution to my own question.
var addresses = db.Addresses.Where(a=>a.EmployeeId == id).ToList();
To make the above call asynchronous I changed it to:
var addresses = await db.Addresses.Where(a=>a.EmployeeId == id).ToListAsync();
The entire method will look like following:
public async Task<ActionResult> FindAddress(int? id)
{
if(id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var addresses = await db.Addresses.Where(a=>a.EmployeeId == id).ToListAsync();
if (addresses == null)
return HttpNotFound();
return View(addresses);
}
I'm starting with REDIS and the StackExchange Redis client. I'm wondering if I'm getting the best performance for getting multiple items at once from REDIS.
Situation:
I have an ASP.NET MVC web application that shows a personal calendar on the dashboard of the user. Because the dashboard is the landing page it's heavily used.
To show the calendar items, I first get all calendar item ID's for that particular month:
RedisManager.RedisDb.StringGet("calendaritems_2016_8");
// this returns JSON Serialized List<int>
Then, for each calendar item id I build a list of corresponding cache keys:
"CalendarItemCache_1"
"CalendarItemCache_2"
"CalendarItemCache_3"
etc.
With this collection I reach out to REDIS with a generic function:
var multipleItems = CacheHelper.GetMultiple<CalendarItemCache>(cacheKeys);
That's implemented like:
public List<T> GetMultiple<T>(List<string> keys) where T : class
{
var taskList = new List<Task>();
var returnList = new ConcurrentBag<T>();
foreach (var key in keys)
{
Task<T> stringGetAsync = RedisManager.RedisDb.StringGetAsync(key)
.ContinueWith(task =>
{
if (!string.IsNullOrWhiteSpace(task.Result))
{
var deserializeFromJson = CurrentSerializer.Serializer.DeserializeFromJson<T>(task.Result);
returnList.Add(deserializeFromJson);
return deserializeFromJson;
}
else
{
return null;
}
});
taskList.Add(stringGetAsync);
}
Task[] tasks = taskList.ToArray();
Task.WaitAll(tasks);
return returnList.ToList();
}
Am I implementing pipelining correct? The REDIS CLI monitor shows:
1472728336.718370 [0 127.0.0.1:50335] "GET" "CalendarItemCache_1"
1472728336.718389 [0 127.0.0.1:50335] "GET" "CalendarItemCache_2"
etc.
I'm expecting some kind of MGET command.
Many thanks in advance.
I noticed an overload method for StringGet that accepts a RedisKey[]. Using this, I see a MGET command in the monitor.
public List<T> GetMultiple<T>(List<string> keys) where T : class
{
List<RedisKey> list = new List<RedisKey>(keys.Count);
foreach (var key in keys)
{
list.Add(key);
}
RedisValue[] result = RedisManager.RedisDb.StringGet(list.ToArray());
var redisValues = result.Where(x=>x.HasValue);
var multiple = redisValues.Select(x => DeserializeFromJson<T>(x)).ToList();
return multiple;
}
I'm working on an API being developed with .net Web Api 2. I've seen many blog posts and SO questions about Web Api version 1, but answers using the changes made in version 2 seem to be scarce by comparison.
Compare these two ways of handling 'errors' in a controller ItemsController
A. Using methods that create objects from System.Web.Http.Results
// GET api/user/userID/item/itemID
[Route("{itemID:int}", Name="GetItem")]
[ResponseType(typeof(ItemDTO))]
public IHttpActionResult Get(int userID, int itemID)
{
if (userID < 0 || itemID < 0) return BadRequest("Provided user id or item id is not valid");
ItemDTO item = _repository.GetItem(itemID);
if (item == null) return NotFound();
if (item.UserID != userID) return BadRequest("Item userID does not match route userID");
return Ok<ItemDTO>(item);
}
B. Throwing exceptions that can be caught by registering a custom Global Exception Handler
// ex) in WebApiConfig.cs
// config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
public class GlobalExceptionHandler : ExceptionHandler
{
public override void Handle(ExceptionHandlerContext context)
{
Exception exception = context.Exception;
HttpException httpException = exception as HttpException;
if (httpException != null)
{
context.Result = new SimpleErrorResult(context.Request, (HttpStatusCode)httpException.GetHttpCode(), httpException.Message);
return;
}
if (exception is RootObjectNotFoundException)
{
context.Result = new SimpleErrorResult(context.Request, HttpStatusCode.NotFound, exception.Message);
return;
}
if (exception is BadRouteParametersException || exception is RouteObjectPropertyMismatchException)
{
context.Result = new SimpleErrorResult(context.Request, HttpStatusCode.BadRequest, exception.Message);
return;
}
if (exception is BusinessRuleViolationException)
{
context.Result = new SimpleErrorResult(context.Request, (HttpStatusCode)422, exception.Message);
return;
}
context.Result = new SimpleErrorResult(context.Request, HttpStatusCode.InternalServerError, exception.Message);
}
}
GET api/user/userID/item/itemID
[Route("{itemID:int}", Name="GetItem")]
[ResponseType(typeof(ItemDTO))]
public IHttpActionResult Get(int userID, int itemID)
{
if (userID < 0 || itemID < 0)
throw new BadRouteParametersException("Provided user or item ID is not valid");
ItemDTO item = _repository.GetItem(itemID);
if (item.UserID != userID)
throw new RouteObjectPropertyMismatchException("Item userID does not match route userID");
return Ok<ItemDTO>(item);
}
Both of these seem like valid options. Since I am able to return System.Web.Http.Results objects it seems like solution A. is the best one.
But consider when in my _repository my GetItem method is implemented like so
public ItemDTO GetItem(int itemId)
{
ItemInfo itemInfo = ItemInfoProvider.GetItemInfo(itemId);
if (itemInfo == null) throw new RootObjectNotFoundException("Item not found");
ItemDTO item = _autoMapper.Map<ItemDTO>(itemInfo);
return item;
}
Here I can skip calling the autoMapper on null in GetItem and also skip checking for null in the controller.
Questions
Which way makes more sense?
Should I attempt a combination of A & B?
Should I try to keep my Controllers thin or should this type of validation & processing logic be kept there since I have access to the NotFound() and BadRequest() methods?
Should I be performing this type of logic somewhere else in the framework pipeline?
I realize my question is more architectural rather than 'how do i use this feature' but again, I haven't found too many explanations of how and when to use these different features.
From my standpoint, a global exception handler makes unit testing each action easier (read: more legible). You're now checking against a specific [expected] exception versus (essentially) comparing status codes. (404 vs. 500 vs. etc.) It also makes changes/logging of error notifications (at a global/unified level) much easier as you have a single unit of responsibility.
For instance, which unit test do you prefer to write?
[Test]
public void Id_must_not_be_less_than_zero()
{
var fooController = new FooController();
var actual = fooController.Get(-1);
Assert.IsInstanceOfType(actual, typeof(BadRequestResult));
}
[Test]
[ExpectedException(typeof(BadRouteParametersException))]
public void Id_must_not_be_less_than_zero()
{
var fooController = new FooController();
var actual = fooController.Get(-1);
}
Generally speaking, I would say this is more a preference than a hard-and-fast rule, and you should go with whatever you find to be the most maintainable and easiest to understand from both an on-boarding perspective (new eyes working on the project) and/or later maintenance by yourself.
As Brad notes, this partly comes down to preference.
Using HTTP codes is consistent with how the web works, so it's the way I lean.
The other consideration is that throwing exceptions has a cost. If you're OK with paying that cost, and take that into account in your design, it's fine to make that choice. Just be aware of it, particularly when you're using exceptions for situations that aren't really exceptional but rather are things you know you may encounter as part of normal application flow.
It's an older post, but there's an interesting discussion on the topic of exceptions and performance here:
http://blogs.msdn.com/b/ricom/archive/2006/09/14/754661.aspx
and the follow-up:
http://blogs.msdn.com/b/ricom/archive/2006/09/25/the-true-cost-of-net-exceptions-solution.aspx
i am working on an asp.net mvc web application. and i have the following repository method,, where i will be passing the .Include() dynamically :-
public async Task<SecurityRole> FindSecurityRole(int id,string path="")
{
return await context.SecurityRoles.Include(path).SingleOrDefaultAsync(a2 => a2.SecurityRoleID == id);
}
now inside my controller i want to call the above method as follow:-
await uniteofwork.SecurityRoleRepository.FindSecurityRole(id.Value,)
but i am not sure what are the apporachies i can follow to pass the properties ?
Thanks
You can chain calls to things like Include over multiple lines by storing the result in a variable. Nothing will actually hit your database until you call an evaluating expression like SingleOrDefaultAsync here.
var query = context.SecurityRoles;
foreach (var include in path.Split(',', StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(include);
}
return await query.SingleOrDefaultAsync(a2 => a2.SecurityRoleID == id);
Splitting the string allows you to pass multiple include hierarchies at once, comma-delimited.
I am writing an MVC 4 application, and using Entity Framework 4.1. I have a validation question which I cannot seem to find the answer to.
Essentially, I have an Entity (object) called "Product" which contains a field "Name", which must follow strict naming conventions which are defined in a separate Entity called "NamingConvention". When the user enters a value, the system needs to check it against the rules established in the NamingConvention entity, and return an error if need be.
Where should this validation be done, and how? I need to check the NamingConvention entity when doing the validation, which means I would need a database context since I'm referencing a different entity. Is there any validation method which won't require me to create a new context? I was thinking of doing the validation in the Controller, since it already creates a data context, but this doesn't seem like the right place to do it.
Thanks for any help!
I have done things like this using a JQuery post (ajax) call from the webpage where the name is being entered. You then post (the value of name) to a method on your controller which can return a JSON value that contains a flag saying if the validation passed and also a message that you want to return to your user. For example :
Javascript in webpage :
$("#name").change(function () {
var nameVal = $(this).val();
$.post(getRoot() + "/NameController/ValidateName", { name: nameVal },
function (data) {
if (data.valid == "true") {
alert("A valid name was chosen");
} else
{
alert(data.message);
}
}, "json");
});
Controller (NameController) Code :
[HttpPost]
public ActionResult ValidateName(string name)
{
// actual validation carried out in a static utility class (Utils.IsNameValid)
// if you are loading the same validation rules from your table each time
// consider caching the data in the application cache or a static List.
bool nameIsValid = Utils.IsNameValid(name, out string ErrorMessage);
JsonResult result = new JsonResult();
result.Data = new { valid = (nameIsValid "true" : "false"), message = ErrorMessage };
return result;
}
I'm using EF 5 but believe you can use this method ... apologies in advance if I'm misleading you with this answer.
You could do the validation within your context (or a context decorator)
public override int SaveChanges()
{
var products = this.GetChangedProducts();
foreach (var product in products)
{
this.ValidateName(product);
}
return base.SaveChanges();
}
private IEnumerable<Product> GetChangedProducts()
{
return (
from entry in _context.ChangeTracker.Entries()
where entry.State != EntityState.Unchanged
select entry.Entity)
.OfType<Product>();
}
private void ValidateName(Product product)
{
//validate here
}