Entity Framework 6 decimal precision - entity-framework-6

In my code-first entity framework sql database I have a decimal value specified as 18,3.
Database object:
public decimal? Kolhalt { get; set; }
In OnModelCreating I have
modelBuilder.Entity<Skada>().Property(o => o.Kolhalt).HasPrecision(18, 3);
In the database the value is specified as Kolhalt (decimal(18, 3), null)
If I enter a value directly in the database I get three decimals, but when I insert or update a value with EntityFramework it is truncated to 2 decimal places.
With Sql server profiler I can see that EF specifies 2 decimals for the stored value:
[Kolhalt] = #14 ... #14 decimal(18,2) ... #14=5.12
(The entered value was 5.123).
So for some reason EF truncates the values sent to the database when I call SaveChanges().
Do I have to specify the number of decimals someplace else to get it right in SaveChanges()?
Best regards,
Henrik

I had a similar problem in a project. Oddly, when I changed my model from decimal? to decimal it started working. I also found a configuration setting TruncateDecimalsToScale which can be added:
public class DbContextConfiguration : DbConfiguration
{
public DbContextConfiguration()
{
var providerInstance= SqlProviderServices.Instance;
SqlProviderServices.TruncateDecimalsToScale = false;
this.SetProviderServices(SqlProviderServices.ProviderInvariantName, SqlProviderServices.Instance);
}
}

Related

decimal datafields throw validation error (ASP.Net MVC)

I have a question about (Clientside?) validation of datafields.
My model has a decimal property
public decimal CalculatedWork { get; set; }
If I try to insert a decimal value like 20,00 I get an error "Value has to be numeric".
If I insert 20, everything is fine and when refreshing, 20,00 is shown.
How can I get this work?
I tried This DataFormatString and this DecimalModelBinder but nothing works.
The DecimalModelBinder isn't even hit.
What can I do or where is my fault?

How to identify column throwing System.Data.SqlClient.SqlException? (conversion of a datetime2 data type to a datetime data type)

I have overridden my ApplicationDbContext.SaveChanges() method. This helps me by providing immediately visible error messages on the yellow and white error application error screen.
But when there is a validation error with a DateTime column, the catch clause doesn't seem to be activated.
Why is that? And how might I identify the invalid column?
Override method
public partial class ApplicationDbContext
{
public override int SaveChanges()
{
try
{
return base.SaveChanges(); //**error is thrown here**//
}
catch (DbEntityValidationException ex)
{
var sb = new StringBuilder();
foreach (var failure in ex.EntityValidationErrors)
{
sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());
foreach (var error in failure.ValidationErrors)
{
sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);
sb.AppendLine();
}
}
throw new DbEntityValidationException(
"Entity Validation Failed - errors follow:\n" +
sb.ToString(), ex
);
}
}
}
However the exception isn't thrown with DateTime columns, it appears that the catch block is not being activated.
TL;DR
If you use Sql DATETIME columns, add validation to .Net DateTime properties on persisted entities to ensure values are between 1753 and 9999 prior to .SaveChanges()
Otherwise, change your Sql storage to DATE or DATETIME2, depending on your precision requirements.
In Detail
Given that the Sql Server DateTime data type can only store dates in the range 1753 - 9999, whereas the .Net DateTime struct can store a larger dynamic range, and higher precision, i.e. not all .Net DateTimes can be stored in Sql Server DATETIME.
Especially prone to this error is a .Net DateTime property on an EF entity which hasn't explicitly been assigned, as default(DateTime) is 0001/01/01.
Hence the preference to replace DATETIME columns with DATETIME2 or DATE storage as appropriate.
As a result, on a > Sql 2005 RDBMS, Entity Framework will default to a Sql DATETIME2 datatype as this will allow storage beyond the limited range offered by DATETIME and is closer to the .Net data type.
However, if you do have an existing table with a DATETIME column and you attempt to bind an 'out of band' DateTime value, annoyingly, Sql Server doesn't actually list the miscreant column name. If you are able to run Sql Profiler while you execute the statement (or paste to LinqPad), you can grab the actual Sql. But the likely candidate is a DateTime which hasn't been set to > 1753 (e.g. is unitialized as a default DateTime).
The reason why you aren't catching the Exception is because of the disjoint between catch DbEntityValidationException vs a thrown SqlException.
If I'm not mistaken you are using smalldatetime in MSSQL, which in some cases can throw this error.
Check this thread for the answer.
Try catching on catch(Exception ex) and there you'll be able to see the exact error message.

MVC DataAnnotation Accept No Spaces

I'm developing a Login View in MVC5.
I would like to set at the ViewModel level a DataAnnotation to state the the field does NOT accept empty spaces.
Is there a Data Annotation in MVC (something like [NoSpaces] that can be used to NOT allow a string field to contain "spaces" ?
How about this:
[RegularExpression(#"^\S*$", ErrorMessage = "No white space allowed")]
Well, the simplest but robust thing I can think of is to look at how existing code works, or how existing data annotation work.
For example, let's look at the System.ComponentModel.DataAnnotations.StringLengthAttribute class.
Here's the definition only (just to keep short):
namespace System.ComponentModel.DataAnnotations
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class StringLengthAttribute : ValidationAttribute
{
public StringLengthAttribute(int maximumLength);
public int MinimumLength { get; set; }
public override string FormatErrorMessage(string name);
public override bool IsValid(object value);
}
}
So I would simply copy the original implementation source code from GitHub and customize it to my needs. For example, to obtain a signature like this (if I understood correctly and this is what you wanted):
public StringLengthAttribute(int maximumLength, int minLength = 0, allowEmptySpaces = true);
For more in-depth info, I would also read the Microsoft docs on the ValidationAttribute class, which is your base class for custom validation data annotations.
EDIT:
I would NOT rely on Regular Expressions to validate data in cases where I just need to exclude empty strings or strings containing only white space(s), because Regular Expressions are very expensive to process (and require a lot of memory allocation, because of an expression compiler, a state machine, etc.).
If this is convenient for anyone, I got pass this issue by doing this on the client:
//trim each form input
var $form = $("#myForm");
$form.find("input:text").each(function(){
var $self= $(this);
$self.va($self.val().trim());
});
//then validate before submit
if($form.valid()){ $form.submit(); }
// on the server ModelState.Isvalid does verify each item with [Required] and won't accept only white spaces from an input (but this means additional roundtrip to server and it's not always convenient)

client side decimal validation isn't working as it should

I have a MVC4 application with unobstrusive javascript client-side validation. This is working fine except for decimal validation. I am encoutering some strange and annoying behaviour.
The decimal validation only allows numbers with three numbers after the decimal separator. However i want this to work with two numbers after the separator (or just any number is fine as it should do by default if i'm correct).
so 1.222 is valid and 1.22 and isn't while i wan't it to be valid. (1.2222 is neither valid).
in my view i have a normal:
#Html.TextBoxFor(x => x.BasePrice)
my model:
[Required]
public decimal BasePrice { get; set; }
and my controller:
ProductVM model = new ProductVM()
{
BasePrice = product.BasePrice
};
product is an Entity Framework 4 entityobject
I've also tried putting;
[DisplayFormat(DataFormatString = "{0:n2}", ApplyFormatInEditMode = true)]
in my model. But this doesn't work either. It has no effect.
I've never seen any behaviour like this when working with MVC. I have no idea where to look. Any clues?
I still don't know what the problem is, but I fixed it with a little bit dirty workaround:
$.validator.methods.number = function (value, element) {
if(!isNaN(parseFloat(strValue)))
{
return true;
}
return false;
}
by overriding the client side decimal validation. just added this in a script block in my _layout.cshtml.
server side validation will ensure the security is still ok by complete validation.

ASP.NET MVC: dealing with Version field

I have a versioned model:
public class VersionedModel
{
public Binary Version { get; set; }
}
Rendered using
<%= Html.Hidden("Version") %>
it gives:
<input id="Version" name="Version" type="hidden" value=""AQID"" />
that looks a bit strange. Any way, when the form submitted, the Version field is always null.
public ActionResult VersionedUpdate(VersionedModel data)
{
...
}
How can I pass Version over the wire?
EDIT:
A naive solution is:
public ActionResult VersionedUpdate(VersionedModel data)
{
data.Version = GetBinaryValue("Version");
}
private Binary GetBinaryValue(string name)
{
return new Binary(Convert.FromBase64String(this.Request[name].Replace("\"", "")));
}
Related posts I found.
Link
Suggests to turn 'Binary Version' into 'byte[] Version', but some commenter noticed:
The problem with this approach is that
it doesn't work if you want to use the
Table.Attach(modified, original)
overload, such as when you are using a
disconnected data context.
Link
Suggests a solution similar to my 'naive solution'
public static string TimestampToString(this System.Data.Linq.Binary binary)
{ ... }
public static System.Data.Linq.Binary StringToTimestamp(this string s)
{ ... }
http://msdn.microsoft.com/en-us/library/system.data.linq.binary.aspx
If you are using ASP.Net and use the
SQL Server "timestamp" datatype for
concurrency, you may want to convert
the "timestamp" value into a string so
you can store it (e.g., on a web
page). When LINQ to SQL retrieves a
"timestamp" from SQL Server, it stores
it in a Binary class instance. So you
essentially need to convert the Binary
instance to a string and then be able
to convert the string to an equivalent
Binary instance.
The code below provides two extension
methods to do this. You can remove the
"this" before the first parameter if
you prefer them to be ordinary static
methods. The conversion to base 64 is
a precaution to ensure that the
resultant string contains only
displayable characters and no escape
characters.
public static string ConvertRowVersionToString(this Binary rowVersion) {
return Convert.ToBase64String(rowVersion.ToArray());
}
public static Binary ConvertStringToRowVersion(this string rowVersion) {
return new Binary(Convert.FromBase64String(rowVersion));
}
I think the problem with not seeing it in the bound model on form submission is that there is no Convert.ToBinary() method available to the model binary to restructure the data from a string to it's binary representation. If you want to do this, I think that you'll need to convert the value by hand. I'm going to guess that the value you are seeing is the Base64 encoding of the binary value -- the output of Binary.ToString(). In that case, you'll need to convert it back from Base64 to a byte array and pass that to the Binary() constructor to reconstitute the value.
Have you thought about caching the object server-side, instead? This could be a little tricky as well as you have to detach the object from the data context (I'm assuming LINQ) or you wouldn't be able to reattach it to a different data context. This blog entry may be helpful if you decide to go that route.
You may need to use binding to get a strongly-typed parameter to your action method.
Try rendering using:
<%=Html.Hidden("VersionModel.Version")%>
And defining your action method signature as:
public ActionResult VersionedUpdate([Bind(Prefix="VersionModel")] VersionedModel data)
{
...
}
This post http://forums.asp.net/p/1401113/3032737.aspx#3032737 suggests to use
LinqBinaryModelBinder from http://aspnet.codeplex.com/SourceControl/changeset/view/21528#338524.
Once registered
protected void Application_Start()
{
ModelBinders.Binders.Add(typeof(Binary), new LinqBinaryModelBinder());
}
the binder will automatically deserialize Version field
public ActionResult VersionedUpdate(VersionedModel data)
{ ... }
rendered this way:
<%= Html.Hidden("Version") %>
(See also http://stephenwalther.com/blog/archive/2009/02/25/asp.net-mvc-tip-49-use-the-linqbinarymodelbinder-in-your.aspx)
There are many ways like here
byte[] b = BitConverter.GetBytes(DateTime.Now.Ticks);//new byte [(DateTime.Now).Ticks];
_store.Version = new System.Data.Linq.Binary(b)
(make sure you bind exclude your version),
But the best way is to let the DB handle it...
There are many ways like here
byte[] b = BitConverter.GetBytes(DateTime.Now.Ticks);//new byte [(DateTime.Now).Ticks]; _store.Version = new System.Data.Linq.Binary(b)
(make sure you bind exclude your version),
But the best way is to let the DB handle it...

Resources