In entity framework, if you set the precision on a decimal column in your database to say 10,2, and update it in your EDMX, it will automatically truncate any decimals to 2 decimal places, correct?
I would prefer that it blow up in my face with an exception if I give it a number with a bunch of decimal places than have it silently truncate it, because it means I've not validated my data properly.
Is there a setting that I can set this? or am I going to have to attach to the savingchanges method and do some reflection?
To clarify with an example:
If I had a table called Invoice with a column called Amount. In my database this is a decimal(10,2) column, this is reflected in the EDMX, my entity says that this has a precision of 10 and a scale of 2.
Then in my code, lets say the user creates an invoice and they enter 10.23456 as the amount, I've forgotten to add some sort of clientside validation for this, so the amount gets sent to the server, my controller then saves an invoice with an amount of 10.23456. Entity framework will truncate this to 10.23 and be done with it.
What I want is this:
If I were to try and save an invoice with 10.23456 as the amount, EF sees that I have got a more precise value than my entity allows, and throws an exception. So this way my failure to validate inputs properly is discovered straight away.
You can«t make this configuration on the Entity Framework. But if you override your class and add some validation metadata on the properties, it will blow on you face with the validation error.
For example
[RegularExpression(#"^([\w-\.]+)#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$",
ErrorMessage = "ERROR MESSAGE")]
public string Email { get; set; }
OR
[StringLength(255, MinimumLength = 0, ErrorMessage = "ERROR MESSAGE")]
public string FriendlyName { get; set; }
Related
Recently we've migrated one of our project from ADO.net to EntityFramework 7. In the log file we've noticed multiple warnings like the following,
The decimal property 'ObjectId' is part of a key on entity type
'AppointmentSchedule'. If the configured precision and scale don't
match the column type in the database, this will cause values to be
silently truncated if they do not fit in the default precision and
scale. Consider using a different property as the key, or make sure
that the database column type matches the model configuration and
enable decimal rounding warnings using 'SET NUMERIC_ROUNDABORT ON'
Did some research and as recommended in forums, tried the following approaches
tried setting,
.HasColumnType("numeric(9, 0)");
.HasPrecision("numeric(9, 0)")
Also tried settings the data annotation on the EF model property
[Column(TypeName = "numeric(9, 0)")]
public decimal ObjectId { get; set; }
None of the above has helped. we are still seeing the warnings. Any suggestion how to avoid those warnings?
One solution is to use DbContextOptionsBuilder.ConfigureWarnings. Check answer from this GitHub issue and Microsoft docs
var contextOptions = new DbContextOptionsBuilder<MyDbContext>()
.ConfigureWarnings(w => w.Ignore(SqlServerEventId.DecimalTypeKeyWarning))
.UseSqlServer(#"...connectionString")
.Options;
using var context = new MyDbContext(contextOptions);
You have a decimal type property and a trying to store it's data in a SQL numeric type with 9 digits and 0 decimal places. This means that it will not be possible to store anything after the decimal point. The problem is that ObjectId should probably be an int not a decimal.
Also since the max int size is 2147483647, you should make the SQl type allow 10 digits:
[Column(TypeName = "numeric(10, 0)")]
We are implementing BigDecimal in C#, reason is existing decimal type has limitations to hold only 29 digits. So for this the first change we have made in SQL table columns. We have defined decimal SQL columns Qty as decimal (38,23) and our ORM class's property is defined as
[Column(Storage = "_Qty", DbType = "Decimal(38,23) NOT NULL", UpdateCheck = UpdateCheck.Never)]
public decimal Qty
{
//get; set;
}
This Qty is a field in Trade table. Now when we try to read this using DataContext as AppDataContext.GetTable<Trade>() it gives conversion overflow exception because SQL column is 38,23 which is not supported in C#.
Now the question is - How can we map custom BigDecimal for ORM property as existing decimal type is not supporting?
If you need more clarification, will share whatever required more.
This has been asked and answered numerous times, but I have a special situation where I have nullable integers in my model.
Model:
[Range(0, int.MaxValue, ErrorMessage = "Please enter valid integer number")]
public int? Jan { get; set; }
In my edit form, if enter "x", then the browser default error pop-up with "Please enter a number" appears. My own client-side validation doesn't appear to be kicking in. If I enter nothing, then it blows up server-side because a parameter was expected in my repository code.
I need it to validate non-integers on the client and I also need it to handle nulls, when someone tries to submit the form with an empty value. I cannot resort to a Required data annotation, because if I do, no data will be loaded. This is a conversion from legacy code to MVC.
UPDATE - CLARIFICATION:
I'm dealing with a lot of nullable ints. The decision to try and make them required was mine - am open to alternative options. I cannot change the int? in the model for various reasons. So, I need to validate against nulls on the client and server to ensure that integers are entered.
You can use the HTML5 required attribute client side to prevent empty values from being entered. If you really want to ensure no empty values are sent to the server, I believe you are going to need to do some server side validation as well.
HTML5 Required Attribute
I have a pretty bazar one for you guys that I just can't figure out or even wrap my head around. I've created a few Azure databases in the past and I believe this is my second one using a .NET backend. I seem to have everything pulling/inserting correctly except for floating point variables.
Condensed class client side:
class MealItem
{
public string ID { get; set; }
public string Name { get; set; }
public float Price { get; set; }
}
Condensed class backend side:
public class MealItem : EntityData
{
public string Name { get; set; }
public float Price { get; set; }
}
And on the server side, the Price column (and other floats of the class) are set to the Data Type "float" with nulls allowed.
I am using sync tables, so the error is shown running this line:
await client.GetSyncTable<MealItem>().PullAsync("All", client.GetSyncTable<MealItem>().CreateQuery(),
CancellationToken.None);
I also tried this for the heck of it:
await client.GetTable<MealItem>().ToListAsync();
Everything works when I comment out the floating point variables on both the client and backend. I've spent a good amount of time on this one and can't seem to figure it out. Any ideas would be greatly appreciated! Thanks!
That error is related to a failed conversion from varchar to float data type that takes place when you are inserting data into the table. You wll have to validate "price" data before you can insert it to the table. If the TRY_CONVERT returns NULL you cannot insert that record because price value is not valid.
Foreshadowing While continuing to search for my problem, I looked at my database in SSMS and noticed that my "float" was taking 8 bytes.
The steps I took leading up to finding the issue were as follows. First, on the backend, I logged a Query().toString(); to get the SQL string being sent to the SQL database. Something like:
SELECT[Extent1].[Id] AS [Id],[Extent1].[Name] AS [Name],[Extent1].[ItemType] AS [ItemType], [Extent1].[Price] AS [Price],[Extent1].[Version] AS [Version],[Extent1].[CreatedAt] AS [CreatedAt],[Extent1].[UpdatedAt] AS [UpdatedAt],[Extent1].[Deleted] AS [Deleted]FROM [dbo].[MealItems] AS [Extent1]
I tried logging the result of this statement as an error but got nothing. Trying to poke around with the solution #albert Morillo posted, I tried
SELECT[Extent1].[Id] AS [Id],[Extent1].[Name] AS [Name],[Extent1].[ItemType] AS [ItemType], Try_convert(float,[Extent1].[Price]) AS [Price],[Extent1].[Version] AS [Version],[Extent1].[CreatedAt] AS [CreatedAt],[Extent1].[UpdatedAt] AS [UpdatedAt],[Extent1].[Deleted] AS [Deleted]FROM [dbo].[MealItems] AS [Extent1]
but still got no result. I finally had the bright of putting this statement in a try catch and logging the error there. It spat back the following:
"Error: The 'Price' property on 'MealItem' could not be set to a 'System.Double' value. You must set this property to a non-null value of type."
Not knowing what this meant, I looked for a double column type in SSMS. Not finding one, I decided to change my floats to doubles on the backend app. Magically, this seems to have done the trick.
I'm not sure if this is the proper solution, but it appears to work for me. Makes sense though since the SQL database is saving and 8 byte number and a C# double is 8 bytes.
I'm trying to store a value with three decimals in an MsSQL database, but for some reason the third decimal is always changed to a 0. I can't figure out why.
The project was created by somebody else with Entity Framework code first v4.0.30319 and it created a column in the database [Amount] [decimal](18, 2) NULL. I have manually changed the database to [Amount] [decimal](18, 3) NULL
In the code:
var _yourbid = form["YourBid"]; // value = 10.123
decimal yourbid;
if (decimal.TryParse(_yourbid, out yourbid))
{
Bid b = new Bid();
b.Amount = yourbid;
//yourbid = 10.123
//b.Amount = 10.123
Db.Bids.Add(b);
Db.Save();
//in database is 10.120
}
Now I expected that the code first somewhere declared the decimal to have a scale of 2, but I couldn't find anything. I checked the options listed in Decimal precision and scale in EF Code First but it's not used.
There isn't a trigger on the database that might be changing it either. I can put in the right value directly from SQL
I must be missing something obvious, but I hope you can point me in the right direction.
This is likely because your Entity Framework model and the underlying database no longer match. For an Entity Framework Code First project, you should update your model first and then use the migration feature to propagate the change to the database. Before this you should change the Amount field on the DB table to have a precision of 2 again so the difference can be detected.
To update your model, see this SO answer on how to customise the precision of a decimal field:
Decimal precision and scale in EF Code First
(Things are easier in later versions of EF, so you may want to consider upgrading at some point in the future.)
You should then add a migration, which will record the SQL actions to apply to the database. Use Add-Migration in the Package Manager Console to do this scaffolding step. Finally, you should also Update-Database to execute the change on the target database.
There's more background information on the web, including this tutorial from Microsoft: https://msdn.microsoft.com/en-gb/data/jj591621.aspx
Edit:
Here's some migration code which will perform the precision change. It may be required to create this manually if you're running an older version of EF and can't use HasPrecision:
public partial class PrecisionChange : DbMigration
{
public override void Up()
{
AlterColumn("dbo.SomeTable", "Amount", (c => c.Decimal(false, 18, 3)));
}
public override void Down()
{
AlterColumn("dbo.SomeTable", "Amount", (c => c.Decimal(false, 18, 2)));
}
}