Remove functionality from ASP.NET MVC controller - c#

I have a controller which is used to save data in database. The controller looks like below:
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
var _employee = new Employee
{
Employee = User.Identity.GetUserId(),
DateTime = DateTime.Parse(string.Format("{0} {1}", viewModel.Date, viewModel.Time))
};
_context.Employees.Add(_employee);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
I want to remove this line of code
DateTime.Parse(string.Format("{0} {1}", viewModel.Date, viewModel.Time))
and make this calculations somewhere else in order to keep the controller clean.
Which is the best way to archive this?

From the data given I see that you have used a ViewModel called EmployeeFormViewModel to saperate the logic from the model. I would guess that your ViewModel looks something like below:
public class EmployeeFormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
}
Now, in order to make the changes in controller, i would suggest you make it look like below:
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
var _employee = new Employee
{
Employee = User.Identity.GetUserId(),
DateTime = viewModel.DateTime
};
_context.Employees.Add(_employee);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
and after that go to your ViewModel and add the new property that you added in the Controller (DateTime). Now your ViewModel should look something like below:
public class EmployeeormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
public DateTime DateTime
{
get
{
return DateTime.Parse(string.Format("{0} {1}", Date, Time));
}
}
}
I hope this solves your problem.

To offer a different perspective, I'd suggest you could put it in an extension method. The concept of combining date and time strings doesn't really feel like it should belong to your domain model, it feels like a generic thing that you might want to use across your application (or even in other applications). I would do this...
public static class DateTimeExtensions
{
public static DateTime ParseToDateTime(this string date, string time = null)
{
return string.IsNullOrEmpty(withTime) ? DateTime.Parse(date) : DateTime.Parse($"{date} {time}");
}
}
And in the controller...
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
var _employee = new Employee
{
Employee = User.Identity.GetUserId(),
DateTime = viewModel.Date.ParseToDateTime(viewModel.Time)
};
EDIT: Additionally...to incorporate etr's answer, which is also a good approach, you could combine the two...
public class EmployeeormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
public DateTime DateTime
{
get
{
return Date.ParseToDateTime(Time);
}
}
}

Rich domain is the way.
public class Employee
{
public Employee(int id, object date, object time)
{
Id = id;
DateTime = DateTime.Parse(string.Format("{0} {1}", date, time))
}
public int Id { get; protected set; }
public DateTime DateTime { get; protected set; }
}
And them:
[Authorize]
[HttpPost]
public ActionResult Create(EmployeeFormViewModel viewModel)
{
_context.Employees.Add(new Employee(User.Identity.GetUserId(), viewModel.Date, viewModel.Time));
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}

I like strong type binding and a Post method as follows:
public ActionResult Create(EmployeeFormViewModel viewModel)
{
viewModel.Post(User.Identity.GetUserId());
_context.Employees.Add(_employee);
_context.SaveChanges();
return RedirectToAction("Index", "Home");
}
The view model looking like this:
public class EmployeeFormViewModel
{
Employee Employee { get; set; }
DateTime Date { get; set; }
DateTime Time { get; set; }
public void Post(int empid)
{
Employee= new Employee
{
EmployeeID = empid,
DateTime = DateTime.Parse(string.Format("{0} {1}", Date, Time))
};
return;
}
}
This is all possible because of the nice MVC Binding engine which generates the EmployeeFormViewModel based on query strings, prior to calling the action method.
I put a "Post" method in all of my ViewModels and let MVC do the work.

Related

ASP.Net MVC How to generate a list of dates

I am trying to generate a list of dates into a selectList in Asp.Net MVC5. I would like to have week commencing list for only 5 weeks in a row but am hitting real problems on how to go about this.
I ideally I would need this in my create ActionMethod because I want to use this against time recorded for that week.
I have been trying to use the following example How can I get the DateTime for the start of the week? and am running into difficulties.
What I have is
Model:
public class TimeSheet
{
public int TimeSheetId { get; set; }
public DateTime WeekCommencing { get; set; }
public int MondayHours { get; set; }
public int TuesdayHours { get; set; }
public int WednesdayHours { get; set; }
public int ThursdayHours { get; set; }
public int FridayHours { get; set; }
public int SaturdayHours { get; set; }
public int SundayHours { get; set; }
public bool CompletedTimeSheet { get; set; }
public int PlanId { get; set; }
public virtual ICollection<Plan> Plan { get; set; }
}
Controller: Create Method
// GET: TimeSheets/Create
public ActionResult Create()
{
DateTime today = DateTime.Today;
if(today.DayOfWeek == DayOfWeek.Monday && today.Day <= 7)
ViewBag
return View();
}
// POST: TimeSheets/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "TimeSheetId,WeekCommencing,MondayHours,TuesdayHours,WednesdayHours,ThursdayHours,FridayHours,SaturdayHours,SundayHours,CompletedTimeSheet,PlanId")] TimeSheet timeSheet)
{
if (ModelState.IsValid)
{
db.TimeSheets.Add(timeSheet);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(timeSheet);
}
Please can someone help me or advise
Many thanks
Mark
Not sure exactly what you mean by "5 weeks in a row", so I just did previous 5 weeks. Completely untested, so if any problems then say.
Edit: Edited so only next 5 Mondays get taken.
It's a bit ambiguous as to what you want as you haven't posted what you have tried.
public class TimeSheet
{
public DateTime DateSelected { get; set; }
}
public ActionResult Create()
{
int weekCount = 5;
List<DateTime> listDates = new List<DateTime>();
for (int i = 0; i < (weekCount * 7); ++i) //Get next 5 weeks
{
//Adds only next 5 mondays to the list of dates
if (DateTime.Today.AddDays(i).DayOfWeek == DayOfWeek.Monday)
listDates.Add(DateTime.Today.AddDays(i));
}
ViewData["DateList"] = new SelectList(listDates);
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "TimeSheetId,WeekCommencing,MondayHours,TuesdayHours,WednesdayHours,ThursdayHours,FridayHours,SaturdayHours,SundayHours,CompletedTimeSheet,PlanId,DateSelected")] TimeSheet timeSheet)
{
if (ModelState.IsValid)
{
db.TimeSheets.Add(timeSheet);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(timeSheet);
}
#Html.DropDownListFor(x => x.DateSelected, (SelectList)ViewData["DateList"], new {#class = "form-control"})
#Html.ValidationMessageFor(x => x.DateSelected, "", new {#class = "text-danger"})

asp.net MVC 4 simple sorting

I am using ASP.net MVC4 and am trying to accomplish a simple sort , so far I have found how to make the whole database sortable - by using ActionLink buttons in my View (failed to make it work btw...), but what I am trying to accomplish is a permanently sorted database.
My view and controller are both scaffolded at the moment, no changes made to this part. I am trying to make the record with the least TimeRemaining to always show up on top of the list.
Thanks!
My Database:
public class EquipmentDataBase {
public int ID { get; set; }
[DisplayName("Name")]
public string equipment { get; set; }
[DisplayName("Inv.Nr.")]
public string InventoryNumber { get; set; }
[DisplayName("Location")]
public string Location { get; set; }
[Required(ErrorMessage ="Please specify date")]
[DataType(DataType.Date)]
[DisplayName("Next Inspection")]
public DateTime NextInspectionDate { get; set; }
[Required(ErrorMessage ="PleaseSpecifyRegistrationDate")]
[DataType(DataType.Date)]
public DateTime RegistrationDate { get; set; }
public string Responsible { get; set; }
public TimeSpan TimeRemaining
{
get
{
return (NextInspectionDate - DateTime.Today);
}
}
My Controller:
namespace PeriodicInspections.Controllers {
public class EquipmentDBController : Controller
{
private EquipmentDbContext db = new EquipmentDbContext();
// GET: EquipmentDB
public ActionResult Index()
{
return View(db.equipment.ToList());
}
If you want to have it sorted by the same criteria, the best approach is to sort them at database level. You can achieve this by changing the code in the controller as follows:
// GET: EquipmentDB
public ActionResult Index()
{
return View(db.equipment.OrderBy(x => x.NextInspectionDate).ToList());
}
The OrderBy sorts the data by NextInspectionDate. This property is present at database level in contrast to the helper property TimeRemaining that is only available in the .NET code. As regards the sort order, this will make no difference.
Use Linq. instead of
return View(db.equipment.ToList());
use
return View(db.equipment.ToList().OrderBy(e=>e.TimeRemaining );

Passing complex object with RouteValueDictionary using RedirectToAction

I'm trying to pass a Complex Object(Object within an object) to my action method using RedirectToAction method but it is returned null. Is there a way to do this using RouteValueDictionary?
My Model:
public class ModelUser
{
public UserLine User { get; set; }
}
public class UserLine
{
public int? UserID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
My Action method:
[HttpPost]
public async Task<ActionResult> Create(UserCreate model)
{
var valDTORegister = new RegisterLine() { Email = model.Email, Password = model.Password, OwnerType = model.OwnerType };
var result = await PostRequest<RegisterLine, UserLine>("http://localhost:10853/", "api/user/Create", valDTORegister);
var usermodel = new ModelUser();
usermodel.User = result;
return RedirectToAction("ProfileUser", new RouteValueDictionary(usermodel));
}
public ActionResult ProfileUser(ModelUser usermodel) //User object is null
{
return View();
}
I tried passing only UserLine object using RouteValueDictionary and the values has been properly passed to my ProfileUser method. This is good but I want to pass the whole ModelUser object because I may need to add more object within it.
you can connect the ProfileUser page to model ModelUser and than call this view like this:
return View("ProfileUser", usermodel);
the action shold be without parameters
public ActionResult ProfileUser()
{
return View();
}
And all data is in the model ("ModelUser") of ProfileUser

ASP.NET MC3 - single interface to create a record and the supporting records

I have the beginnings of an ASP.NET MVC3 application that is supposed to implement scheduling. I have the following in my model:
public class Schedule
{
public int ScheduleID { get; set; }
public bool isDisabled { get; set; }
[DisplayName("For Delivery")]
public bool isDeliver { get; set; }
public int Count { get; set; }
public virtual ICollection<TimeofDay> Times { get; set; }
public virtual Location Location { get; set; }
public int Week { get; set; }
public int weekday { get; set; }
}
public class TimeofDay
{
[Key]
public int TimeID {get;set;}
public DateTime Time { get; set; }
}
The model is supposed to accept 0 or more Time of Day entities that I pass by using JavaScript to create a new input field:
function createtimefield() {
var TimeDiv = document.getElementById('timefields');
var newDivInput = document.createElement("input");
newDivInput.name = "Times";
idText="Time" + GLOBAL_timeDivIdCount++;
newDivInput.id = idText;
newDivInput.value = "12:00 am";
TimeDiv.appendChild(newDivInput);
}
My Controller will work file for accepting the data passed up until I add data to the time fields. This is supposed to create new entities in the TimeofDay table that gets generated by the model, and link back to the the ScheduleID. I don't want two interfaces to input this simple data, but can't seem to find the way to create both entities with MVC3 in a single action. Anyone have any ideas?
Andrew
(Thank you for reading)
As requested the controller was:
public ActionResult Create(Schedule schedule, string[] schedTimes)
{
if (ModelState.IsValid)
{
Schedule newschedule = db.Schedule.Add(schedule);
db.SaveChanges();
return RedirectToAction("Index");
}
......
}
I now realize I need to create a view model that will encompass both my schedule class and an array of strings. I will create the schedule and then iterate through the array of strings and create TimeofDay objects
for inputs to bind with times collection ur form fields need to have indexed names like
Times[0].TimeID // this would probably be hidden field
Times[0].Time
Times[1].TimeID
Times[1].Time
.
.
Times[n].TimeID
Times[n].Time
when you do this there might be other issues when deleting rows from the middle. there are lot of blog posts on this out there. Phil haack's post is my favorite as it explains in very simple way how can you have non sequential indices for list binding. For more links please look at my answer to this question
I attempted to create a view model to support this by extending my model as follows:
public class ScheduleCreate
{
public Schedule schedule {get;set;}
public string[] schedTimes {get;set}
}
I then modified my view by changing:
#model scheduler.Models.Schedule
to:
#model scheduler.Models.ScheduleCreate
I additionally changed all of the model.(parameter) to model.schedule.(parameter)
Then I changed my controller to:
public ActionResult Create(ScheduleCreate mod)
{
if (ModelState.IsValid)
{
Schedule newschedule = db.Schedule.Add(mod.schedule);
if (mod.schedTime != null)
{
foreach (string instanceTime in mod.schedTimes)
{
newschedule.Times.Add(new {Time = DateTime.Parse(instanceTime) }); // unteseted
}
}
db.SaveChanges();
return RedirectToAction("Index");
}
PopulateDropDown();
return View(mod.schedule);
}

View and ICollection Question

I am coverting my app from webforms to mvc, at the moment i am at a design issue (well i just dont know how to do it in mvc).
Basically my model would be something like this:
public class DamagedItem
{
public Int32 LoanId {get;set;}
public String IdentityCode {get;set;}
public virtual ICollection<DamagedItems> DamagedItems {get;set;}
}
In my controller i would like to do:
public ActionResult Add(DamagedItem damagedItem)
{
//Do update logic here
}
Then in my view i can add to the ICollection as needed.
But, i can't do this because if i try and access the ICollection from my controller it is null.
Here is an image of when i want to do:
I just dont know how to lay it out in my view, how to i add such items to my ICollection, update the view then when i need to save i have access to what i have added from my controller?
Thanks,
Nick
Edit:
I was thinking of using a partial in the view and doing all the logic for the bottom half using ajax and storing it in a session variable, but i would prefer NOT to make it reliant on ajax.
It is better to separate: you shoud have 2 actions, which produce 2 view.
You should have LoadInformationModel classe:
public class LoadInformationModel
{
public string StudentCode { get; set; }
public string FirstName { get; set; }
// etc..
public ICollection<Damage> Type { get; set; }
}
corresponding action
[HttpGet]
public ActionResult LoanInformation(int id)
{
var loanInfo = // get data by given id..
var model = new LoadInformationModel {
StudentCode = loanInfo.StudentCode,
// etc
Type = new List<Damage> { new Damage { Value = "Damaged"}, new Damage { Value = "Damaged Again" }
}
return View(model);
}
As well as RepairDataModel class
public class RepairDataModel
{
public bool CoveredByWarranty { get; set; }
public ICollection Status { get; set; }
}
And corresponding action
[HttpGet]
public ActionResult Repair(int id)
{
// logic
return View(model);
}
Your task is to create Post handler, that would save data to DB then form submitted
[HttpPost]
public ActionResult(RepairDataModel model)
{
// save to db
return View();
}
The view returned by Index() method, could be created like
#Html.RenderAction("LoanInformation")
#Html.RenderAction("Repair")
The rest depends on your desing and imagination. I hope that would give you direction.
What I can see is only the DamagedItem lacks a contructor with values for Collection;
public class DamagedItem
{
public DamagedItem()
{
DamagedItems = new List<DamagedItems>();
DamagedItems.Add(new DamagedItem { Description = "Damaged" } );
}
public Int32 LoanId {get;set;}
public String IdentityCode {get;set;}
public virtual ICollection<DamagedItems> DamagedItems {get;set;}
}

Categories

Resources