I have a Date field in my DB and I'm trying to update it to the current Date when I press the submit button on my webpage but it does not update. I believe I'm doing the correct steps but here is my code.
Controller:
public ActionResult TakeInventory(int? AssetNum, string owners, string locationId, string clientId)
{
ViewBag.LocationId = new SelectList(db.Locations, "LocationKey", "LocationName");
ViewBag.ClientId = new SelectList(db.ClientSites, "ClientSiteKey", "ClientSiteName");
var records = from s in db.Assets select s;
if (AssetNum != 0)
{
records = records.Where(c => c.AssetKey == AssetNum);
}
if (!String.IsNullOrEmpty(owners))
{
records = records.Where(x => x.InventoryOwner.Equals(owners));
}
if (!String.IsNullOrEmpty(locationId))
{
int locnum = Convert.ToInt32(locationId);
records = records.Where(x => x.LocationKey == locnum);
}
if (!String.IsNullOrEmpty(clientId))
{
int clinum = Convert.ToInt32(clientId);
records = records.Where(x => x.ClientSiteKey == clinum);
}
else
{
return View(records);
}
return View(records);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult TakeInventory([Bind(Include = "InventoryDate")] Asset asset)
{
if (ModelState.IsValid)
{
db.Entry(asset).State = EntityState.Modified;
asset.InventoryDate = DateTime.Now;
db.Assets.Add(asset);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(asset);
}
View:
#foreach (var items in Model)
{
<p>Last Inventory Date: #Html.DisplayFor(modelItem => items.InventoryDate) </p>
<input type="submit" value="Submit" />
Model:
public partial class Asset
{
public System.DateTime InventoryDate { get; set; }
public Asset()
{
InventoryDate = DateTime.Now;
}
}
You want to retrieve the Asset entity again before updating again.
For example,
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult TakeInventory([Bind(Include = "InventoryDate")] Asset asset)
{
if (ModelState.IsValid)
{
var entity = (from s in db.Assets where AssetNum == asset.AssetNum Select s).FirstOrDefalt();
entity.InventoryDate = DateTime.Now;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(asset);
}
Is a bad practice:
asset.InventoryDate = DateTime.Now;
At least, you need:
1. SaveChanges() your DbContext
2. Your DateTime field in backend must be Nullable or NotNull in Db (there is no inmpicit conversion)
But the real trouble is timezones. Its all works fine, if you have only one instance in only one datacenter and all your clients is from only one small and beauty country (one timezone wide)
DateTime.Now returns you local mashine timezone time.
If you use your 'entity.InventoryDate' in any kind of requests query it can return confused rezults, and can be surprized with funny result: for ex., value with tomorrow datetime relatively to you :)
For Web-services always cast to UTC that kind of fields, or use triggers or default expression for this kind of fields inside your DB engine
P.S. Russia is 11 timezones wide, i know what i'm talking about
Why you are passing the Current date , there is no need for that you can you Sql build in function "GETDATE()" to Get the current Date
Related
Using MySQL with EF6 throws an exception when I sum values from an empty child collection as the DefaultIfEmpty is not supported well with MySQL as related in bug #80127.
public class Foo
{
public int Id { get; set; }
public decimal Total { get; set; }
public virtual IList<Bar> Bars { get; set; }
}
public class Bar
{
public int Id { get; set; }
public int FooId { get; set; }
public virtual Foo Foo { get; set; }
public decimal Received { get; set; }
}
Using the recommended approach with DefaultIfEmpty throws an exception with invalid where clausule 'Project1'.'Id'. This is an old bug of MySQL.
var result = db.Foo.Select(f => new {
Total = f.Total,
Received = f.Bars.Select(b => b.Received).DefaultIfEmpty().Sum()
});
I'm using an inline if that works fine but generates an very ugly SQL with lots of inner queries and repetitions of select statements.
var result = db.Foo.Select(f => new {
Total = f.Total,
Received = f.Bars.Any() ? f.Bars.Sum(b => b.Received) : 0
});
Is there a better way to avoid DefaultIfEmpty?
The alternative of DefaultIfEmpty which I usually prefer is using cast operator to promote the non nullable type to nullable, which works (even) with MySQL connector.
Then the solution depends of your receiver class property type.
The best is if you can receive a nullable result, in which case the query is simple:
var result = db.Foo.Select(f => new {
Total = f.Total,
Received = f.Bars.Sum(b => (decimal?)b.Received)
});
If it needs to be a non nullable type, you can use null coalescing operator
var result = db.Foo.Select(f => new {
Total = f.Total,
Received = f.Bars.Sum(b => (decimal?)b.Received) ?? 0
});
but the generated SQL query is ugly and inefficient.
The best you can do in such case is to use (a quite annoying) double select trick:
var result = db.Foo.Select(f => new {
f.Total,
Received = f.Bars.Sum(b => (decimal?)b.Received)
})
.Select(r => new {
r.Total,
Received = r.Received ?? 0
};
or (a quite better) query syntax with let clause:
var result =
from f in db.Foos
let received = f.Bars.Sum(b => (decimal?)b.Received)
select new { f.Total, Received = received ?? 0 };
Tested on latest EF6.1.3 with MySQL Connector/Net 6.9.8
I have the following view model
public class ProjectVM
{
....
[Display(Name = "Category")]
[Required(ErrorMessage = "Please select a category")]
public int CategoryID { get; set; }
public IEnumerable<SelectListItem> CategoryList { get; set; }
....
}
and the following controller method to create a new Project and assign a Category
public ActionResult Create()
{
ProjectVM model = new ProjectVM
{
CategoryList = new SelectList(db.Categories, "ID", "Name")
}
return View(model);
}
public ActionResult Create(ProjectVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Save and redirect
}
and in the view
#model ProjectVM
....
#using (Html.BeginForm())
{
....
#Html.LabelFor(m => m.CategoryID)
#Html.DropDownListFor(m => m.CategoryID, Model.CategoryList, "-Please select-")
#Html.ValidationMessageFor(m => m.CategoryID)
....
<input type="submit" value="Create" />
}
The view displays correctly but when submitting the form, I get the following error message
InvalidOperationException: The ViewData item that has the key 'CategoryID' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'.
The same error occurs using the #Html.DropDownList() method, and if I pass the SelectList using a ViewBag or ViewData.
The error means that the value of CategoryList is null (and as a result the DropDownListFor() method expects that the first parameter is of type IEnumerable<SelectListItem>).
You are not generating an input for each property of each SelectListItem in CategoryList (and nor should you) so no values for the SelectList are posted to the controller method, and therefore the value of model.CategoryList in the POST method is null. If you return the view, you must first reassign the value of CategoryList, just as you did in the GET method.
public ActionResult Create(ProjectVM model)
{
if (!ModelState.IsValid)
{
model.CategoryList = new SelectList(db.Categories, "ID", "Name"); // add this
return View(model);
}
// Save and redirect
}
To explain the inner workings (the source code can be seen here)
Each overload of DropDownList() and DropDownListFor() eventually calls the following method
private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
IDictionary<string, object> htmlAttributes)
which checks if the selectList (the second parameter of #Html.DropDownListFor()) is null
// If we got a null selectList, try to use ViewData to get the list of items.
if (selectList == null)
{
selectList = htmlHelper.GetSelectData(name);
usedViewData = true;
}
which in turn calls
private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)
which evaluates the the first parameter of #Html.DropDownListFor() (in this case CategoryID)
....
o = htmlHelper.ViewData.Eval(name);
....
IEnumerable<SelectListItem> selectList = o as IEnumerable<SelectListItem>;
if (selectList == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
MvcResources.HtmlHelper_WrongSelectDataType,
name, o.GetType().FullName, "IEnumerable<SelectListItem>"));
}
Because property CategoryID is typeof int, it cannot be cast to IEnumerable<SelectListItem> and the exception is thrown (which is defined in the MvcResources.resx file as)
<data name="HtmlHelper_WrongSelectDataType" xml:space="preserve">
<value>The ViewData item that has the key '{0}' is of type '{1}' but must be of type '{2}'.</value>
</data>
according to stephens (user3559349) answer, this can be useful:
#Html.DropDownListFor(m => m.CategoryID, Model.CategoryList ?? new List<SelectListItem>(), "-Please select-")
or in ProjectVM:
public class ProjectVM
{
public ProjectVM()
{
CategoryList = new List<SelectListItem>();
}
...
}
Most Likely Caused some sort of error redirecting to your page and you not initializing your model's drop down lists again.
Make sure that you initialize your drop downs in either the model's constructor or every time before you send said model to the page.
Otherwise you will need to maintain the state of the drop down lists either through the view bag or through the hidden value helpers.
OK, the poster's canned answer neatly explained why the error occurred, but not how to get it to work. I'm not sure that's really an answer, but it did point me in the right direction.
I ran into the same issue and found a slick way to resolve it. I'll try to capture that here. Disclaimer - I work on web pages once a year or so and really don't know what I'm doing most of the time. This answer should in no way be considered an "expert" answer, but it does the job with little work...
Given that I have some data object (most likely a Data Transfer Object) that I want to use a drop-down list to supply valid values for a field, like so:
public class MyDataObject
{
public int id;
public string StrValue;
}
Then the ViewModel looks like this:
public class MyDataObjectVM
{
public int id;
public string StrValue;
public List<SectListItem> strValues;
}
The real problem here, as #Stephen so eloquently described above, is the select list isn't populated on the POST method in the controller. So your controller methods would look like this:
// GET
public ActionResult Create()
{
var dataObjectVM = GetNewMyDataObjectVM();
return View(dataObjectVM); // I use T4MVC, don't you?
}
private MyDataObjectVM GetNewMyDataObjectVM(MyDataObjectVM model = null)
{
return new MyDataObjectVM
{
int id = model?.Id ?? 0,
string StrValue = model?.StrValue ?? "",
var strValues = new List<SelectListItem>
{
new SelectListItem {Text = "Select", Value = ""},
new SelectListITem {Text = "Item1", Value = "Item1"},
new SelectListItem {Text = "Item2", Value = "Item2"}
};
};
}
// POST
public ActionResult Create(FormCollection formValues)
{
var dataObject = new MyDataObject();
try
{
UpdateModel(dataObject, formValues);
AddObjectToObjectStore(dataObject);
return RedirectToAction(Actions.Index);
}
catch (Exception ex)
{
// fill in the drop-down list for the view model
var dataObjectVM = GetNewMyDataObjectVM();
ModelState.AddModelError("", ex.Message);
return View(dataObjectVM);
)
}
There you have it. This is NOT working code, I copy/pasted and edited to make it simple, but you get the idea. If the data members in both the original data model and the derived view model have the same name, UpdateModel() does an awesome job of filling in just the right data for you from the FormCollection values.
I'm posting this here so I can find the answer when I inevitably run into this issue again -- hopefully it will help someone else out as well.
I had the same problem, I was getting an invalid ModelState when I tried to post the form. For me, this was caused by setting CategoryId to int, when I changed it to string the ModelState was valid and the Create method worked as expected.
In my case the first ID in my list was zero, once I changed the ID to start from 1, it worked.
I am trying to use Web API to grab certain fields from my MVC controller. I can't seem to match the right type with the right list. I am fine with converting everything to string.
I either get an error in code (can not convert types), or if I get it to compile, I get this error:
"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'."
From other similar posts, people responded with how to create a list, but not with the declaration of the return value of the Get. Please include both.
Also I would prefer not to add additional controllers as I need to do this on a number of my models.
Here is my code--note you can see I tried a number of different ways:
public class APICLIENTsController : ApiController
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET api/<controller>
public IEnumerable<string> Get()
//public IEnumerable<CLIENT> Get()
{
//return db.CLIENTs.OrderBy(x => x.CLIENTNAME).ToList();
string[] listOfUsers = db.CLIENTs.OrderBy(x => x.CLIENTNAME).Select(r => new
{
ID = r.CLIENTID.ToString(),
NAME = r.CLIENTNAME
});
return listOfUsers.ToList();
//return db.CLIENTs.Select(x => new { x.CLIENTNAME }).ToArray();
}
If you want to return JSON use the
JsonResult
type.
public JsonResult Get()
{
//return db.CLIENTs.OrderBy(x => x.CLIENTNAME).ToList();
string[] listOfUsers = db.CLIENTs.OrderBy(x => x.CLIENTNAME).Select(r => new
{
ID = r.CLIENTID.ToString(),
NAME = r.CLIENTNAME
});
return Json(listOfUsers.ToList(), JsonRequestBehavior.AllowGet);
}
Your query is returning a collection of anonymous objects, not string[] so it will throw an exception. Even if you were to generate string[] by concatenating the CLIENTID and CLIENTNAME properties, it would be a little use to the client.
Create a model to represent what you need to return to the view
public class ClientVM
{
public int ID { get; set; }
public string Name { get; set; }
}
and modify your method to
public IEnumerable<ClientVM> Get()
{
IEnumerable<ClientVM> model = db.CLIENTs.OrderBy(x => x.CLIENTNAME).Select(r => new ClientVM
{
ID = r.CLIENTID,
Name = r.CLIENTNAME
});
return model;
}
Side note: depending on how your calling and consuming this in the client, you may need to change the Content-Type to specifically return json (refer these answers for more detail)
I am using Dapper in my ASP.NET MVC 5 application and in my query I only want 2 fields to return but the Json returns all of the fields. This is my model
public class thread
{
[Key]
public int id { get; set; }
public int? profileID { get; set; }
public int numberkeeper { get; set; }
public int? photocount { get; set; }
}
This is my controller..
[ResponseType(typeof(thread))]
public IHttpActionResult Getstream()
{
string Connectionstring = ConfigurationManager.ConnectionStrings["db"].ConnectionString;
using (System.Data.SqlClient.SqlConnection sqlConnection = new System.Data.SqlClient.SqlConnection(Connectionstring))
{
sqlConnection.Open();
var statevi = sqlConnection.Query<thread>("Select top 5 id,numberkeeper from threads").ToList();
if (statevi == null)
{
return NotFound();
}
return Ok(statevi);
}
}
That code returns Json as it is using .Net Web API,as you can see from the query I only want 2 fields returned. When I run it and see the Json it displays all fields (4) and off course the 2 fields not selected show up as null . I wanted so that the Json only shows the returnn of id and numberkeeper
Create a View Model class:
public class ThreadViewModel
{
public int id { get; set; }
public int numberkeeper { get; set; }
}
Let Dapper know you want it to create the ThreadViewModel for you:
var statevi = sqlConnection.Query<ThreadViewModel>("Select top 5 id,numberkeeper from threads").ToList();
This way you both query the database for the relevant properties and return just them to the client (without Dapper creating the full object with nulls).
If you create a new model that exposes the only two members that you want to render, that will prevent Web API from returning back additional JSON.
You could also convert the data after loading it into a new anonymous model using LINQ.
return Ok(statevi.Select(s => new { s.id, s.numberkeeper }));
If you want to keep the same model, but suppress null valued members Web API allows you to configure the JSON formatting to exclude null properties.
config.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
If you want to use 2 or selected rows from query then you can use query method and extension method...
1. LINQ query method
using (System.Data.SqlClient.SqlConnection sqlConnection = new System.Data.SqlClient.SqlConnection(Connectionstring))
{
sqlConnection.Open();
var statevi = sqlConnection.Query<thread>("Select top 5 id,numberkeeper from threads").ToList();
if (statevi == null)
{
return NotFound();
}
var result = (from d in statevi
select new { d.id, d.numberkeeper }).ToList();
return Ok(result);
}
Extension Method: change this syntax to result of query method of above
var result = query.Select(d => new { d.Id, d.Title }).ToList();
both will give result same.
let me tell if it is working fine for your project or not.
User class:
public class User
{
public DateTime CreatedDate { get; private set; }
public DateTime? AmendDate { get; private set; }
}
I need the columns CreatedDate and AmendDate have the values ​​generated by the database (now() command)
Reading some articles on the internet, advised me to use this in DbContext class on OnModelCreating method:
Property(p => p.CreatedDate)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(p => p.AmendDate)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
But does not work, saying that an error is supported types are rowversion and timespan.
How do you do with these property types?
Any tips?
You can make both columns nullable and Save changes with both columns with null values. You also need set the default value for the column as getdate() in the relevant column in the database. Then these columns will automatically be populated with current date if you try to insert null values. But EF will not update the property with the database generated date value.
Other solution is to set the values before you call the SaveChanges() method
public override int SaveChanges()
{
var changeSet = ChangeTracker.Entries<User>();
if (changeSet != null)
{
foreach (var entry in changeSet
.Where(c => c.State == EntityState.Added || c.State == EntityState.Modified))
{
entry.Entity.CreatedDate = entry.Entity.AmendDate = DateTime.Now;
}
}
return base.SaveChanges();
}