Entity Framework 4.1 Automatic date - asp.net-mvc-3

i'm quite new to Entity Framework (and asp.net mvc 3) and this is my really first experience with EF4.1 code first.
My question is simple: when I generate via model a new table for database I would like to make
Automatically add current datetime to a field when a new row is created.
Automatically update the field every time the field is updated.
Actually, the variable appears like:
[DisplayName("Data Modifica")]
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "{0:d}")]
public DateTime DataModifica { get; set; }
I guess i could write something on "OnModelCreating" event of datacontext but I'm too new to already master this :)
Can someone help?
thanks in advance,
V.

That has nothing to do with creation of model. The "model" is description of your mapping between classes and database. OnModelCreating is used to modify mapping definition, not to modify data. It has nothing to do with data in the entity instance itself.
If you want automatic modification you can override SaveChanges:
public override int SaveChanges()
{
DateTime now = DateTime.Now;
foreach (var entity in ChangeTracker.Entries<YourEntityType>()
.Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)
.Select(e => e.Entity))
{
entity.DateModifica = now; // set the date
}
return base.SaveChanges();
}

Why not define the property to be one with a backing field, which is initialized to the default value ?
private DateTime _dateOfRequest = DateTime.Today;
[Display(Name = "Date of Request"), DataType(DataType.Date)]
public System.DateTime DateOfRequest {
get { return _dateOfRequest; }
set { _dateOfRequest = value; }
}

Related

ASP.NET MVC - Preserving invalid DateTime selection with 3 dropdown lists

I'm still fairly new to ASP.NET and MVC and despite days of googling and experimenting, I'm drawing a blank on the best way to solve this problem.
I wrote a BirthdayAttribute that I want to work similar to the EmailAddressAttribute. The birthday attribute sets the UI hint so that the birthday DateTime will be rendered using an editor template that has 3 dropdown lists. The attribute can also be used to set some additional meta data that tells the year dropdown how many years it should display.
I know I could use jQuery's date picker, but in the case of a birthday I find the 3 dropdowns much more usable.
#model DateTime
#using System;
#using System.Web.Mvc;
#{
UInt16 numberOfVisibleYears = 100;
if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("NumberOfVisibleYears"))
{
numberOfVisibleYears = Convert.ToUInt16(ViewData.ModelMetadata.AdditionalValues["NumberOfVisibleYears"]);
}
var now = DateTime.Now;
var years = Enumerable.Range(0, numberOfVisibleYears).Select(x => new SelectListItem { Value = (now.Year - x).ToString(), Text = (now.Year - x).ToString() });
var months = Enumerable.Range(1, 12).Select(x => new SelectListItem{ Text = new DateTime( now.Year, x, 1).ToString("MMMM"), Value = x.ToString() });
var days = Enumerable.Range(1, 31).Select(x => new SelectListItem { Value = x.ToString("00"), Text = x.ToString() });
}
#Html.DropDownList("Year", years, "<Year>") /
#Html.DropDownList("Month", months, "<Month>") /
#Html.DropDownList("Day", days, "<Day>")
I also have a ModelBinder to rebuild my date afterwards. I've removed the content of my helper functions for brevity, but everything works great up to this point. Normal, valid dates, work just fine for creating or editing my members.
public class DateSelector_DropdownListBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (controllerContext == null)
throw new ArgumentNullException("controllerContext");
if (bindingContext == null)
throw new ArgumentNullException("bindingContext");
if (IsDropdownListBound(bindingContext))
{
int year = GetData(bindingContext, "Year");
int month = GetData(bindingContext, "Month");
int day = GetData(bindingContext, "Day");
DateTime result;
if (!DateTime.TryParse(string.Format("{0}/{1}/{2}", year, month, day), out result))
{
//TODO: SOMETHING MORE USEFUL???
bindingContext.ModelState.AddModelError("", string.Format("Not a valid date."));
}
return result;
}
else
{
return base.BindModel(controllerContext, bindingContext);
}
}
private int GetData(ModelBindingContext bindingContext, string propertyName)
{
// parse the int using the correct value provider
}
private bool IsDropdownListBound(ModelBindingContext bindingContext)
{
//check model meta data UI hint for above editor template
}
}
Now that I'm looking at it, I should probably be using a nullable DateTime, but that's neither here nor there.
The problem I'm having is with very basic validation of invalid dates such as February 30th, or September 31st. The validation itself works great, but the invalid dates aren't ever saved and persisted when the form is reloaded.
What I'd like is to remember the invalid date of February 30th and redisplay it with the validation message instead of resetting the dropdowns to their default value. Other fields, like the email address (decorated with the EmailAddressAttribute) preserve invalid entries just fine out of the box.
At the moment I am just trying to get the server side validation working. To be honest, I haven't even started thinking about the client side validation yet.
I know there is lots I could do with javascript and ajax to make this problem a moot point, but I would still rather have the proper server side validation in place to fall back on.
I finally managed to solve my problem, so I wanted to share my solution.
DISCLAIMER:
Although I used to be great with .NET 2.0 back in the day, I'm only now updating my skills to the latest versions of C#, ASP.NET, MVC, and Entity Framework. If there are better ways to do anything I've done below please I'm always open to feedback.
TODO:
Implement client side validation for invalid dates such as February 30th. Client side validation for [Required] attribute is already built in.
Add support for cultures so that the date shows up in desired format
The solution came to me when I realized that the problem I was having is that DateTime will not allow itself to be constructed with an invalid date such as February 30th. It simply throws an exception. If my date wouldn't construct, I knew of no way to pass my invalid data back through the binder to the ViewModel.
To solve this problem, I had to do away with the DateTime in my view model and replace it with my own custom Date class. The solution below will provide fully functioning server side validation in the event that Javascript is disabled. In the event of a validation error the invalid selections will persist after the validation message is displayed allowing the user to easily fix their mistake.
It should be easy enough to map this view-ish Date class to the DateTime in your date model.
Date.cs
public class Date
{
public Date() : this( System.DateTime.MinValue ) {}
public Date(DateTime date)
{
Year = date.Year;
Month = date.Month;
Day = date.Day;
}
[Required]
public int Year { get; set; }
[Required, Range(1, 12)]
public int Month { get; set; }
[Required, Range(1, 31)]
public int Day { get; set; }
public DateTime? DateTime
{
get
{
DateTime date;
if (!System.DateTime.TryParseExact(string.Format("{0}/{1}/{2}", Year, Month, Day), "yyyy/M/d", CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
return null;
else
return date;
}
}
}
This is just a basic date class that you can construct from a DateTime. The class has properties for Year, Month, and Day as well as a DateTime getter that can try to retrieve you a DateTime class assuming you have a valid date. Otherwise it returns null.
When the built in DefaultModelBinder is mapping your form back to this Date object, it will take care of the Required and Range validation for you. However, we will need a new ValidationAtribute to make sure that invalid dates such as February 30th aren't allowed.
DateValidationAttribute.cs
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public sealed class DateValidationAttribute : ValidationAttribute
{
public DateValidationAttribute(string classKey, string resourceKey) :
base(HttpContext.GetGlobalResourceObject(classKey, resourceKey).ToString()) { }
public override bool IsValid(object value)
{
bool result = false;
if (value == null)
throw new ArgumentNullException("value");
Date toValidate = value as Date;
if (toValidate == null)
throw new ArgumentException("value is an invalid or is an unexpected type");
//DateTime returns null when date cannot be constructed
if (toValidate.DateTime != null)
{
result = (toValidate.DateTime != DateTime.MinValue) && (toValidate.DateTime != DateTime.MaxValue);
}
return result;
}
}
This is a ValidationAttribute that you can put on your Date fields and properties. If you pass in the resource file class and the resource key it will search the corresponding resource file in your "App_GlobalResources" folder for the error message.
Inside the IsValid method, once we're sure we're validating a Date we check it's DateTime property to see if it's not null to confirm that it's valid. I throw in a check for DateTime.MinValue and MaxValue for good measure.
So that's about it really. With this Date class, I managed to do away completely with the custom ModelBinder. This solution relies completely on the DefaultModelBinder, which means all of the validation works right out of the box. It apparently even checks my new DateValidationAttribute, which I was super excited about. I stressed forever thinking I might have to muck with validators in a custom binder. This feels a lot cleaner.
Here is the complete code for the partial view I'm using.
DateSelector_DropdownList.cshtml
#model Date
#{
UInt16 numberOfVisibleYears = 100;
if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("NumberOfVisibleYears"))
{
numberOfVisibleYears = Convert.ToUInt16(ViewData.ModelMetadata.AdditionalValues["NumberOfVisibleYears"]);
}
var now = DateTime.Now;
var years = Enumerable.Range(0, numberOfVisibleYears).Select(x => new SelectListItem { Value = (now.Year - x).ToString(), Text = (now.Year - x).ToString() });
var months = Enumerable.Range(1, 12).Select(x => new SelectListItem { Text = new DateTime(now.Year, x, 1).ToString("MMMM"), Value = x.ToString() });
var days = Enumerable.Range(1, 31).Select(x => new SelectListItem { Value = x.ToString(), Text = x.ToString() });
}
#Html.DropDownList("Year", years, "<Year>") /
#Html.DropDownList("Month", months, "<Month>") /
#Html.DropDownList("Day", days, "<Day>")
I'll also include the attribute I use that sets up the template hint and the number of visible years to show.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class DateSelector_DropdownListAttribute : DataTypeAttribute, IMetadataAware
{
public DateSelector_DropdownListAttribute() : base(DataType.Date) { }
public void OnMetadataCreated(ModelMetadata metadata)
{
metadata.AdditionalValues.Add("NumberOfVisibleYears", NumberOfVisibleYears);
metadata.TemplateHint = TemplateHint;
}
public string TemplateHint { get; set; }
public int NumberOfVisibleYears { get; set; }
}
I think the solution turned out a lot cleaner than I expected it to. It solves all of my problems in the exact way that I was hoping to. I do wish that I was somehow able to keep the DateTime, but this is the only way I could figure out how to maintain an invalid selection using only server side code.
Are there any improvements you would make?

update single value in EF 4.0

i m working on MVC application and using Entity Framework 4.0 for database connection.While editing record i want to update some fields in table. code for edit is
[HttpPost]
public ActionResult Edit(ProjectActivityDetail projectactivitydetail, FormCollection formcollection)
{
if (ModelState.IsValid)
{
string value = Request["ChkIntBool"];
if (value.Substring(0, 4) == "true") { projectactivitydetail.IsApproved = 1; } else { projectactivitydetail.IsApproved = 0; }
projectactivitydetail.ProjectActivityDID = long.Parse(Session["ProjectActivityDID"].ToString());
projectactivitydetail.UpatedBy = long.Parse(Session["UserID"].ToString());
projectactivitydetail.UpdatedON = System.DateTime.Now;
db.Entry(projectactivitydetail).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.FK_ProjectActivityID = new SelectList(db.ProjectActivityMasters, "ProjectActivityID", "ActivityName", projectactivitydetail.FK_ProjectActivityID);
return View(projectactivitydetail);
}
i just want to update these fields but when this executred my other fields updated to null , is there any way i can keep those values as it is and update these many values in database table.please help
It looks like the entity being passed into the service call is not complete. My advice would be to find the existing entity and make the changes to that rather than attaching the one passed in:
[HttpPost]
public ActionResult Edit(ProjectActivityDetail projectactivitydetail, FormCollection formcollection)
{
if (ModelState.IsValid)
{
//I'm assuming the PK of the entity is id so adjust for that
var entity = db.Set<ProjectActivityDetail>().Find(projectactivitydetail.id);
string value = Request["ChkIntBool"];
entity.IsApproved = value.Substring(0, 4) == "true" ? 1 : 0;
entity.ProjectActivityDID = long.Parse(Session["ProjectActivityDID"].ToString());
entity.UpatedBy = long.Parse(Session["UserID"].ToString());
entity.UpdatedON = System.DateTime.Now;
db.Entry(entity).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.FK_ProjectActivityID = new SelectList(db.ProjectActivityMasters, "ProjectActivityID", "ActivityName", projectactivitydetail.FK_ProjectActivityID);
return View(projectactivitydetail);
}
Yes, there's a way. Add hidden fields to your view with the data you want to persist on the model, int this case ProjectActivityDetail, this way the properties will get bound to the projectactivitydetail variable on the model binding stage.
Suppose you have a property called CreatedOn that is being set to null, on your view you'd add:
#Html.HiddenFor(model => model.CreatedOn)
Then, when you submit the form the value originally on CreatedOn will be bound to the property at projectactivitydetail and your model will be saved just fine. Your fields are being updated to null because model binding is not finding any values for them.
Good luck.

Single property not getting bound on HttpPost

I'm working on the first MVC3 project at our company, and I've hit a block. No one can seem to figure out what's going on.
I have a complex Model that I'm using on the page:
public class SpaceModels : List<SpaceModel> {
public bool HideValidation { get; set; }
[Required(ErrorMessage=Utilities.EffectiveDate + Utilities.NotBlank)]
public DateTime EffectiveDate { get; set; }
public bool DisplayEffectiveDate { get; set; }
}
In the Controller, I create a SpaceModels object with blank SpaceModels for when Spaces get combined (this would be the destination Space).
// Need a list of the models for the View.
SpaceModels models = new SpaceModels();
models.EffectiveDate = DateTime.Now.Date;
models.DisplayEffectiveDate = true;
models.Add(new SpaceModel { StoreID = storeID, SiteID = siteID, IsActive = true });
return View("CombineSpaces", models);
Then in the View, I am using that SpaceModels object as the Model, and in the form making a TextBox for the Effective Date:
#model Data.SpaceModels
#using (Html.BeginForm("CombineSpaces", "Space")) {
<div class="EditLine">
<span class="EditLabel LongText">
New Space Open Date
</span>
#Html.TextBoxFor(m => m.EffectiveDate, new {
size = "20",
#class = "datecontrol",
// Make this as a nullable DateTime for Display purposes so we don't start the Calendar at 1/1/0000.
#Value = Utilities.ToStringOrDefault(Model.EffectiveDate == DateTime.MinValue ? null : (DateTime?)Model.EffectiveDate, "MM/dd/yyyy", string.Empty)
})
#Html.ValidationMessageFor(m => m.EffectiveDate)
</div>
<hr />
Html.RenderPartial("_SpaceEntry", Model);
}
The Partial View that gets rendered iterates through all SpaceModels, and creates a containing the Edit fields for the individual SpaceModel objects. (I'm using the List to use the same Views for when the Spaces get Subdivided as well.)
Then on the HttpPost, the EffectiveDate is still back at it's DateTime.MinValue default:
[HttpPost]
public ActionResult CombineSpaces(SpaceModels model, long siteID, long storeID, DateTime? effectiveDate) {
// processing code
}
I added that DateTime? effectiveDate parameter to prove that the value when it gets changed does in fact come back. I even tried moving the rendering of the TextBox into the _SpaceEntry Partial View, but nothing worked there either.
I did also try using the #Html.EditorFor(m => m.EffectiveDate) in place of the #Html.TextBoxFor(), but that still returned DateTime.MinValue. (My boss doesn't like giving up the control of rendering using the #Html.EditorForModel by the way.)
There has to be something simple that I'm missing. Please let me know if you need anything else.
Looking at the source code for DefaultModelBinder, specifically BindComplexModel(), if it detects a collection type it will bind the individual elements but will not attempt to bind properties of the list object itself.
What model binding does is attempt to match the names of things or elements in the view to properties in your model or parameters in your action method. You do not have to pass all of those parameters, all you have to do is add them to your view model, then call TryUpdateModel in your action method. I am not sure what you are trying to do with SpaceModel or List but I do not see the need to inherit from the List. Im sure you have a good reason for doing it. Here is how I would do it.
The view model
public class SpacesViewModel
{
public DateTime? EffectiveDate { get; set; }
public bool DisplayEffectiveDate { get; set; }
public List<SpaceModel> SpaceModels { get; set; }
}
The GET action method
[ActionName("_SpaceEntry")]
public PartialViewResult SpaceEntry()
{
var spaceModels = new List<SpaceModel>();
spaceModels.Add(
new SpaceModel { StoreID = storeID, SiteID = siteID, IsActive = true });
var spacesVm = new SpacesViewModel
{
EffectiveDate = DateTime.Now,
DisplayEffectiveDate = true,
SpaceModels = spaceModels
};
return PartialView("_SpaceEntry", spacesVm);
}
The POST action method
[HttpPost]
public ActionResult CombineSpaces()
{
var spacesVm = new SpacesViewModel();
// this forces model binding and calls ModelState.IsValid
// and returns true if the model is Valid
if (TryUpdateModel(spacesVm))
{
// process your data here
}
return RedirectToAction("Index", "Home");
}
And the view
<label>Effective date: </label>
#Html.TextBox("EffectiveDate", Model.EffectiveDate.HasValue ?
Model.EffectiveDate.Value.ToString("MM/dd/yyyy") : string.empty,
new { #class = "datecontrol" })
Sometimes you need to explicitly bind form data using hidden fields such as
#Html.HiddenField("EffectiveDate", Model.EfectiveDate.)
In order to bind the properties of the SpaceModel object you can add individual properties such as SiteID to the view model or add a SpaceModel property for a single SpaceModel. If you want to successfully bind a complex model, add it as a Dictionary populated with key-value pairs rather than a List. You should then add the dictionary to the view model. You can even add a dictionary of dictionaries for hierarchical data.
I hope this helps :)

EF 4.3.1 Code First Updating Related Tables

Update:
This is now driving me crazy!
After much Googling etc. I am really no closer to a solution.....
However I have found one thing that is puzzling me even more - the "States" of the entities just before the m_dbContext.SaveChanges() call. (see below for full repository code)
var updateInfoState = m_dc.Entry(oldPage.UpdateInfo).State; // State is 'Modified'
var oldPageState = m_dc.Entry(oldPage).State; // State is 'Detached'
this.m_dc.SaveChanges();
Why is "oldPage" detached?
Getting quite desperate now!! ;)
Original:
I appear to be having a problem with EF Code-First updating related tables correctly.
In this simplified example, the 'UpdateInfo' table IS being updated OK with the new DateTime .... but the 'Pages' table is not being updated with the new 'Name' value.
I am seeding code-first POCOs via DropCreateDatabaseAlways / override Seed ... and EF is creating the test tables correctly - so at this point it seems to know what it is doing....
I am sure this is something simple/obvious I am missing!
All help very much appreciated!
My Class definitions:
public class Page
{
public int Id { get; set; }
public string Name { get; set; }
public virtual UpdateInfo UpdateInfo { get; set; } // virtual For Lazy loading
}
[Table("UpdateInfo")] // much better than EF's choice of UpdateInfoes!
public class UpdateInfo
{
public int Id { get; set; }
public DateTime DateUpdated { get; set; }
}
public class DomainContext : DbContext
{
public DbSet<Page> Pages { get; set; }
public DbSet<UpdateInfo> UpdateInfo { get; set; }
}
Tables created by Code-First
Pages Table
===========
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](max) NULL,
[UpdateInfo_Id] [int] NULL,
UpdateInfo Table
================
[Id] [int] IDENTITY(1,1) NOT NULL,
[DateUpdated] [datetime] NOT NULL,
My Repository code:
public Page Get(int id)
{
Page page = m_dbContext.Pages.Single(p => p.Id == id);
return page;
}
public void Update(PagePostModel model)
{
Page oldPage = Get(model.PageModel.Id); // on oldPage Name = "Hello", DateUpdated = "Last Year"
Page newPage = Mapper.Map<PageModel, Page>(model.PageModel); // on newPage Name = "Goodbye" (AutoMapper)
newPage.UpdateInfo = oldPage.UpdateInfo; // take a copy of the old UpdateInfo since its not contained in the model
newPage.UpdateInfo.DateUpdated = DateTime.UtcNow; // update to now
oldPage = newPage; // copy the updated page we grabbed from dbContext above (NB Everything looks great here..oldPage is as expected)
m_dbContext.SaveChanges(); // update - only the 'UpdateInfo' table is being updated - No change to 'Pages' table :(((
}
As you know, there is a change tracker api in Entity Framework.
To track the changes of your entities you retrieved from the database, DbContext uses its reference value.
Your "Update" function above inserts newPage into oldPage. So, DbContext never knows oldPage is a newPage. So, it is "detached".
However, for UpdateInfo, it is copy of reference in oldPage, so DbContext can track change of that. So, it is "modified".
To solve this problem, how about using the code below?
Page newPage = Mapper.Map<PageModel, Page>(model.PageModel);
oldPage.UpdateInfo = newPage.UpdateInfo;
oldPage.UpdateInfo.DateUpdated = DateTime.UtcNow;
m_dbContext.SaveChanges();
Update
Then, use Attach & Detach methods.
Those methods help you attach and detach entities from DbContext.
Page newPage = Mapper.Map<PageModel, Page>(model.PageModel);
// if you attach first, there will be an exception,
// because there is two entities having same id.
m_dbContext.Entry(oldPage).State = EntityState.Detached;
m_dbContext.Pages.Attach(newPage);
// if you don't set IsModified = true,
// DbContext cannot know it is changed.
m_dbContext.Entry(newPage).State = EntityState.Modified;
m_dbContext.SaveChanges();

ASP.NET MVC change model data type in before send to view

I used Linq to Entity, for fetch a model from DB
var works = db.Work.Where(a => a.StartDate == DateTime.Now).ToList();
this model contain a DateTime that I want to change it to string before send it to view
beacuase need to show it as a persianDate Time,
like this :
foreach (var item in workslist)
{
item.StartDate = "1391/01/01"; //Just For Exapmle as you know this won't work
}
return View(workslist);
So is there any way to change a data type in a strongly typed model? or other way to do this except save DateTime as String in my DB
You can use the [DisplayFormat] attribute with EditorFor/DisplayFor helpers to format the data.
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy/mm/dd}")]
public DateTime StartDate { get; set; }
Then in view:
#Html.EditorFor(m => m.StartDate)
OR, you can also consider to define separate ViewModel for your 'Work' domain model. Though this approach results in more code, it's provide more flexibility and considered as good practice.
Here is sample code:
public class Work
{
public DateTime StartDate {get;set;}
}
public class WorkViewModel
{
public WorkViewModel()
{
}
public string StartDate { get; set; }
public static WorkViewModel Map(Work domainModel)
{
return new WorkViewModel() {
//Apply your Date format logic
StartDate = domainModel.StartDate.ToString("yyyy/MM/dd")
};
}
}
In action method:
var works = db.Work.Where(a => a.StartDate == DateTime.Now).ToList();
var workViewModels = new List<WorkViewModel>(works.Count);
foreach (var work in works)
workViewModels.Add(WorkViewModel.Map(work));
return View(workViewModels);
you can have a function in your controller to change datetime to string
public string changeDateTimeToPersianString ( DateTime time)
{
// your code
}
then you can call this function from your View.
in Razor something like this
#changeDateTimeToPersianString(workslistitem.StartDate)

Resources