Three-Tier Architecture: Get All Data and Validations - asp.net-mvc

The project I am working is 'University Management System' and it's a big one. Right now, I am implementing the student registration section that works fine (A small portion of the project). I've used 'Three-Tier Architecture' and 'ORM - EF' in ASP.NET MVC template. In the project, I need to do some validations for registering students depending upon their year, department etc. So there are sections like DAL, BLL, finally controller and view. I've done the validations in the controller and getting the data from BLL that again retrieves data from DAL (This is the simple condition of 'Three-Tier Architecture'). So my questions are:
1) Is it OK to do the validations in the controller?
2) If not and need to do it in the BLL, will it be just fine and why or I can
continue doing it in the controller?
Note: To me, doing the validations in the controller or BLL seems OK and the same. Does it have any effect?
Right now, I've done the following:
DAL:
public List<Student> Add(int studentID, string studentName, string email, DateTime regDate)
{
List<Student> lst = null;
Student aStudent = new Student();
aStudent.StudentID = studentID;
aStudent.StudentName = studentName;
aStudent.Email = email;
aStudent.RegDate = regDate;
try
{
db.Students.Add(aStudent);
db.SaveChanges();
}
catch (Exception ex)
{
ex.ToString();
}
return lst;
}
BLL:
public List<Student> Add(int studentID, string studentName, string email, DateTime regDate)
{
return aStudentGateway.Add(studentID, studentName, email, regDate);
}
Controller:
/**Student Registration - Starts**/
[HttpPost]
public ActionResult AddStudent(Student aStudent)
{
List<Department> departments = aDepartmentManager.GetAllDepartments();
List<DepartmentViewModel> departmentsViewModel = aDepartmentManager.GetAllDepartmentViewModel();
DateTime yearInDateTime = Convert.ToDateTime(Request.Form["RegDate"]);
string extractYear = yearInDateTime.ToString();
var year = DateTime.Parse(extractYear).Year;
int department = Convert.ToInt32(Request.Form["Department"]);
List<Student> studentList = aStudentManager.GetAllStudents();
int count = 1;
var query = (from c in studentList
where c.Department == department && c.Year == year
select c).ToList();
foreach (var c in query)
{
if (query.Count() > 0)
{
int m = Convert.ToInt32(c.StudentID);
count = m + 1; //Incrementing the numbers by one with the table column
}
else
{
int m = 1;
count = m + 1; //Incrementing the numbers by one with the variable assigned one
}
}
Student student = new Student();
student.StudentName = Request.Form["StudentName"];
student.Email = Request.Form["Email"];
student.RegDate = Convert.ToDateTime(Request.Form["RegDate"]);
student.StudentID = count;
if (aStudentManager.ExistEmailAny(student.Email))
{
ViewBag.ErrorMessage = "Email already exists";
}
else
{
aStudentManager.Add(aStudent.StudentID, aStudent.StudentName, aStudent.Email, aStudent.RegDate);
ViewBag.Message = "Registration successful. See below to verify.";
/**This section used to show student details after registration**/
var result = (from c in departments
join d in departmentsViewModel on c.DepartmentID equals d.DepartmentId
where d.DepartmentId == department
select c);
foreach (var items in result)
{
if (count.ToString().Length > 1)
{
ViewBag.StudentID = items.Code + "-" + year + "-" + "0" + count;
}
else
{
ViewBag.StudentID = items.Code + "-" + year + "-" + "00" + count;
}
StudentViewModel.StudentID = student.StudentID;
StudentViewModel.StudentName = student.StudentName;
StudentViewModel.Email = student.Email;
StudentViewModel.RegDate = student.RegDate;
}
/**This section used to show student details after registration**/
}
return View();
}
/**Student Registration - Ends**/

I would provide multiple steps of validation in the different layers, depending on the context and the meaning of the layer.
First, it's a best practice to provide validation both on client and server side.
For the client side you should provide field checks for required fields and other simple validations. If you are using MVC you can use data annotations.
The same validation should be replicated in the controller. Here you should fail fast applying some kind of contract to the parameters that have been passed. One good practice is using Code Contracts that provide preconditions that need to be satisfied to go on in your pipeline of execution.
In the business layer provide the check that needs to be done in the business logic.
Finally in the data access layer provide all the checks that are needed to persist your data. If you are using EF a good practice is implementing the IValidatableObject for your entity classes. Here in Scott Gu's blog you can find a post that explains this technique.
Even though this approach look like it will introduce repetitions, it will provide consistency in your data and separate concerns between your layers.

1) Is it OK to do the validations in the controller?
No at all, it would be more better to use Data Annotation Validator Attributes, and to do validation in your model class.
Second thing, you're doing some stuff of DAL in your controller, like
List<Department> departments = aDepartmentManager.GetAllDepartments();
List<DepartmentViewModel> departmentsViewModel = aDepartmentManager.GetAllDepartmentViewModel();
var query = (from c in studentList
where c.Department == department && c.Year == year
select c).ToList();
These all queries should be in DAL, which is exact use of DAL to interact with the database, and keep your controller clean.
Third thing,
If you pass Student to the controller, then not need to get each attribute using Request.Form.
Hope this make sense!

Related

Best Way to Update only modified fields with Entity Framework

Currently I am doing like this:
For Example:
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
db.Entry(oldobj).CurrentValues.SetValues(model);
}
It works, but for example,
I have 50 columns in my table but I displayed only 25 fields in my form (I need to partially update my table, with remaining 25 column retain same old value)
I know it can be achieve by "mapping columns one by one" or by creating "hidden fields for those remaining 25 columns".
Just wondering is there any elegant way to do this with less effort and optimal performance?
This is a very good question. By default I have found that as long as change tracking is enabled (it is by default unless you turn it off), Entity Framework will do a good job of applying to the database only what you ask it to change.
So if you only change 1 field against the object and then call SaveChanges(), EF will only update that 1 field when you call SaveChanges().
The problem here is that when you map a view model into an entity object, all of the values get overwritten. Here is my way of handling this:
In this example, you have a single entity called Person:
Person
======
Id - int
FirstName - varchar
Surname - varchar
Dob - smalldatetime
Now let's say we want to create a view model which will only update Dob, and leave all other fields exactly how they are, here is how I do that.
First, create a view model:
public class PersonDobVm
{
public int Id { get; set; }
public DateTime Dob { get; set; }
public void MapToModel(Person p)
{
p.Dob = Dob;
}
}
Now write the code roughly as follows (you'll have to alter it to match your context name etc):
DataContext db = new DataContext();
Person p = db.People.FirstOrDefault();
// you would have this posted in, but we are creating it here just for illustration
var vm = new PersonDobVm
{
Id = p.Id, // the Id you want to update
Dob = new DateTime(2015, 1, 1) // the new DOB for that row
};
vm.MapToModel(p);
db.SaveChanges();
The MapToModel method could be even more complicated and do all kinds of additional checks before assigning the view model fields to the entity object.
Anyway, the result when SaveChanges is called is the following SQL:
exec sp_executesql N'UPDATE [dbo].[Person]
SET [Dob] = #0
WHERE ([Id] = #1)
',N'#0 datetime2(7),#1 int',#0='2015-01-01 00:00:00',#1=1
So you can clearly see, Entity Framework has not attempted to update any other fields - just the Dob field.
I know in your example you want to avoid coding each assignment by hand, but I think this is the best way. You tuck it all away in your VM so it does not litter your main code, and this way you can cater for specific needs (i.e. composite types in there, data validation, etc). The other option is to use an AutoMapper, but I do not think they are safe. If you use an AutoMapper and spelt "Dob" as "Doob" in your VM, it would not map "Doob" to "Dob", nor would it tell you about it! It would fail silently, the user would think everything was ok, but the change would not be saved.
Whereas if you spelt "Dob" as "Doob" in your VM, the compiler will alert you that the MapToModel() is referencing "Dob" but you only have a property in your VM called "Doob".
I hope this helps you.
I swear by EntityFramework.Extended. Nuget Link
It lets you write:
db.Person
.Where(x => x.ID == model.ID)
.Update(p => new Person()
{
Name = newName,
EditCount = p.EditCount+1
});
Which is very clearly translated into SQL.
Please try this way
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
// Newly Inserted Code
var UpdatedObj = (Person) Entity.CheckUpdateObject(oldobj, model);
db.Entry(oldobj).CurrentValues.SetValues(UpdatedObj);
}
public static object CheckUpdateObject(object originalObj, object updateObj)
{
foreach (var property in updateObj.GetType().GetProperties())
{
if (property.GetValue(updateObj, null) == null)
{
property.SetValue(updateObj,originalObj.GetType().GetProperty(property.Name)
.GetValue(originalObj, null));
}
}
return updateObj;
}
I have solved my Issue by using FormCollection to list out used element in form, and only change those columns in database.
I have provided my code sample below; Great if it can help someone else
// Here
// collection = FormCollection from Post
// model = View Model for Person
var result = db.Person.Where(x => x.ID == model.ID).SingleOrDefault();
if (result != null)
{
List<string> formcollist = new List<string>();
foreach (var key in collection.ToArray<string>())
{
// Here apply your filter code to remove system properties if any
formcollist.Add(key);
}
foreach (var prop in result.GetType().GetProperties())
{
if( formcollist.Contains(prop.Name))
{
prop.SetValue(result, model.GetType().GetProperty(prop.Name).GetValue(model, null));
}
}
db.SaveChanges();
}
I still didn't find a nice solution for my problem, so I created a work around. When loading the Entity, I directly make a copy of it and name it entityInit. When saving the Entity, I compare the both to see, what really was changed. All the unchanged Properties, I set to unchanged and fill them with the Database-Values. This was necessary for my Entities without Tracking:
// load entity without tracking
var entityWithoutTracking = Context.Person.AsNoTracking().FirstOrDefault(x => x.ID == _entity.ID);
var entityInit = CopyEntity(entityWithoutTracking);
// do business logic and change entity
entityWithoutTracking.surname = newValue;
// for saving, find entity in context
var entity = Context.Person.FirstOrDefault(x => x.ID == _entity.ID);
var entry = Context.Entry(entity);
entry.CurrentValues.SetValues(entityWithoutTracking);
entry.State = EntityState.Modified;
// get List of all changed properties (in my case these are all existing properties, including those which shouldn't have changed)
var changedPropertiesList = entry.CurrentValues.PropertyNames.Where(x => entry.Property(x).IsModified).ToList();
foreach (var checkProperty in changedPropertiesList)
{
try
{
var p1 = entityWithoutTracking.GetType().GetProperty(checkProperty).GetValue(entityWithoutTracking);
var p2 = entityInit.GetType().GetProperty(checkProperty).GetValue(entityInit);
if ((p1 == null && p2 == null) || p1.Equals(p2))
{
entry.Property(checkProperty).CurrentValue = entry.Property(checkProperty).OriginalValue; // restore DB-Value
entry.Property(checkProperty).IsModified = false; // throws Exception for Primary Keys
}
} catch(Exception) { }
}
Context.SaveChanges(); // only surname will be updated
This is way I did it, assuming the new object has more columns to update that the one we want to keep.
if (theClass.ClassId == 0)
{
theClass.CreatedOn = DateTime.Now;
context.theClasses.Add(theClass);
}
else {
var currentClass = context.theClasses.Where(c => c.ClassId == theClass.ClassId)
.Select(c => new TheClasses {
CreatedOn = c.CreatedOn
// Add here others fields you want to keep as the original record
}).FirstOrDefault();
theClass.CreatedOn = currentClass.CreatedOn;
// The new class will replace the current, all fields
context.theClasses.Add(theClass);
context.Entry(theClass).State = EntityState.Modified;
}
context.SaveChanges();
In EF you can do like this
var result = db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
if(result != null){
result.Name = newName;
result.DOB = newDOB;
db.Person.Update(result);
}
Or you can use
using (var db= new MyDbContext())
{
var result= db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
result.Name= newName;
result.DOB = newDOB;
db.Update(result);
db.SaveChanges();
}
For more detail please EntityFramework Core - Update Only One Field
No Worry guys
Just write raw sql query
db.Database.ExecuteSqlCommand("Update Person set Name='"+_entity.Name+"' where Id = " + _entity.ID + "");

Combining data from two repositories MVC4

I am trying to combine the data from two repositories in MVC4. I am using a join statement in my Action function to gather information from two tables. I am passing this this data onto my view. I am a little confused to how to correctly combine the data from the two repositories. Some help would be appreciated. THANKS!
In controller:
public IOwnerRepository OwnerDB;
public IDwellingRepository DwellingDB;
public OwnerController() : this(new OwnerRepository(), new DwellingRepository()) {}
public OwnerController(IOwnerRepository oRepository, IDwellingRepository dRepository)
{
OwnerDB = oRepository;
DwellingDB = dRepository;
}
Action:
public ActionResult Account(int id, int? activelink)
{
// Link values
// Account = 0
// Listings = 1
// Profile = 2
// Create Property = 3
// Check for a value
// If no value set to zero
if (!activelink.HasValue)
activelink = 0;
// ####ing hackers
if (activelink > 3)
activelink = 0;
ViewBag.ActiveLink = activelink.ToString();
switch (activelink)
{
case 0:
ViewBag.Title = "Account Details";
break;
case 1:
ViewBag.Title = "Listings";
break;
case 2:
ViewBag.Title = "Profile";
break;
case 3:
ViewBag.Title = "Create Property";
break;
}
var oOwner = OwnerDB.FindOne(GetUserId(), id);
if (oOwner == null)
{
return new FileNotFoundResult { Message = "No Account found for id " + id.ToString() };
}
else
{
return View(oOwner);
}
}
Owner Repository:
public Owner FindOne(string UserId, int id)
{
var dwelling = (from o in db.Owner
join d in db.Dwelling on o.ID equals d.Owner_ID into ps
from k in ps.DefaultIfEmpty()
select o );
var oOwner = (from p in dwelling
where p.ID == id
select p);
return oOwner.FirstOrDefault();
}
First question you need to ask yourself is: Do I need those 2 repositories?
Does it make sens in your domain definition to have both of them? Does Dwelling make sense without an Owner and vice versa?
One of the DDD best practices is to limit one repository per aggregate root. Have a look at this SO question where someone was wondering if there was a need to build both User and Phone repostories.
If you do need to have separate repositories then you might have to consider adding a Service layer on top of them.
Please have a look at this SO answer where I answered someone how to decouple things (there's a Unit of Work pattern in this example but it doesn't matter)

DataTables editable cant figure out how to delete/update rows

I am trying to implement dataTables plugin in server-side mode to render a table on my webpage. I am coding ASP.NET with c# and MVC.
I want to edit (delete/update/add) data to the table and write any change to my database.
But in difference to the example of this tutorial: http://www.codeproject.com/Articles/165410/ASP-NET-MVC-Editable-DataTable-jQuery-DataTables-a data provided by my controller does not contain a single primary key, but a compound key:
projectId and
questionId
Here's my controller, which provides data from my database:
public ActionResult AjaxHandler(jQueryDataTableParamModel param)
{
var any = (from pq in _db.ProjectQuestions
join q in _db.Question
on pq.QuestionID equals q.QuestionID
join c in _db.Category
on q.CategoryID equals c.CategoryID
select new
{
projectID = pq.ProjectID,
questionID = q.QuestionID,
categoryName = c.CategoryName,
questionName = q.QuestionName,
questionDescription = q.QuestionDescription
});
int count = any.Count();
var result = new List<object[]>();
return Json(new
{
sEcho = param.sEcho,
iTotalRecords = count,
iTotalDisplayRecords = count,
aaData = any
},
JsonRequestBehavior.AllowGet);
}
So far the standard DeleteData/UpdateData and AddData methods dont even receive any value for id.
public string DeleteData(int id)
{
return "ok";
}
How can I use the CRUD-functionality of Editable if my data has to be identified by two parameters?
So far I couldn't find any solution to edit data. Many thanks in advance.
_tek
I would suggest you to use Grid view function.
Please follow this video:
http://www.youtube.com/watch?v=vZIOI136IKY

Append Text To Drop Down Option Populated from DB

I'm building a page within an MVC application that contains a drop down box that allows the user to select a from a number of financial periods.
For the purposes of making it clearer for our users, I'd like to append "(Current)" to the option that is the current financial period and potentially "(Latest)" to the previous period to represent the last full period. We can already determine which financial period we are in and subsequently the one previous and so on.
So let's say the two options I'm looking to append to currently look like this:
Period 11
Period 12
and I'd like it them to read:
Period 11 (Latest)
Period 12 (Current)
The drop down is populated from a database, but I would like to append this text at the application level.
Is this something that is possible? If so, how? I'm at a bit of a loss as to how to go about it.
Thanks for any help in advance.
I assume you're using some sort of IEnumerable on your model/view model class to populate the items in the drop-down. When you construct this list, simply add your "(Current)" and "(Latest)" options before returning the model to your view. If FinancialPeriod is a model/view model class like this:
public class FinancialPeriod{
public int FinancialPeriodId { get; set; }
public string DisplayText { get; set; }
}
Then you could do this in your controller, assuming that your list of all Financial Periods retrieved from the database is a property on your model called FinancialPeriods:
public ActionResult Index(){
var model = GetTheModel();
model.FinancialPeriods.Add(new FinancialPeriod{
FinancialPeriodId = <something>,
DisplayText = "(Current)"
};
model.FinancialPeriods.Add(new FinancialPeriod{
FinancialPeriodId = <somethingElse>
DisplayText = "(Latest)"
};
return View(model);
}
Make sure you substitute values for <something> and <somethingElse> that you can interpret correctly when the model is posted back to your controller.
Edit - based on your comments and edits, something like this is more appropriate:
public ActionResult Index(){
var model = GetTheModel();
var currentMonth = DateTime.Now.Month;
var previousMonth = currentMonth - 1;
if (previousMonth == 0)
previousMonth = 12;
var currentPeriod = "Period " + currentMonth.ToString();
var latestPeriod = "Period " + previousMonth.ToString();
var newList = new List<FinancialPeriod>();
foreach(var period in model.FinancialPeriods){
if(period.DisplayText == currentPeriod)
{
newList.Add(new FinancialPeriod
{
FinancialPeriodId = period.FinancialPeriodId,
DisplayText = period.DisplayText + " (Current)"
};
continue;
}
if(period.DisplayText == latestPeriod)
{
newList.Add(new FinancialPeriod
{
FinancialPeriodId = period.FinancialPeriodId,
DisplayText = period.DisplayText + " (Latest)"
};
continue;
}
newList.Add(period);
}
model.FinancialPeriods = newList;
return View(model);
}

Entity Framework Debacle

I am developing an MVC application which and I'm not clear on the ideal way to save these entities. What I am doing now works but is nasty to say the least.
My action method takes ViewModels and uses Automapper to map them to the related entities. My entities are Requestor, Order and OrderDetail.
Obviously all these calles to the SaveChanges method are wrong but I have been running into issues of new children being added instead of updated amongst other things.
Any help on how this code should look would really be appreciated.
Jason MacKenzie
[HttpPost]
public ActionResult Edit(FormCollection formValues, RequestorViewModel requestor, OrderViewModel order, List<OrderDetailViewModel> OrderDetails)
{
var query = from r in db.Requestors
where r.RequestorID == requestor.RequestorID
select r;
var req = query.SingleOrDefault();
var orderQuery = from o in db.Orders
where o.RequestorID == requestor.RequestorID
select o;
var or = orderQuery.SingleOrDefault();
List<OrderDetail> orDet = db.OrderDetails.Where(od => od.OrderID == or.OrderID).ToList();
Mapper.CreateMap<RequestorViewModel, Requestor>();
req = Mapper.Map<RequestorViewModel, Requestor>(requestor);
Mapper.CreateMap<OrderViewModel, Order>();
or = Mapper.Map<OrderViewModel, Order>(order);
Mapper.CreateMap<OrderDetailViewModel, OrderDetail>();
orDet = Mapper.Map<List<OrderDetailViewModel>, List<OrderDetail>>(OrderDetails);
foreach (OrderDetail od in orDet)
{
db.OrderDetails.ApplyCurrentValues(od);
db.SaveChanges();
}
db.Requestors.ApplyCurrentValues(req);
db.SaveChanges();
db.Orders.ApplyCurrentValues(or);
db.SaveChanges();
return View("Index");
}
First you can clean up your queries quite a bit.
var req = (from r in db.Requestors
where r.RequestorID == requestor.RequestorID
select r).SingleOrDefault();
var or = (from o in db.Orders.Include("OrderDetails")
where o.RequestorID == requestor.RequestorID
select o).SingleOrDefault();
Take a look at how .Include works to map all your related data. It's very useful.
You should only need to call SaveChanges() once too.

Resources