I have an MVC 3 project. In sql server I have a field called NewsContent which is a text field.
Here is the definition of the property in the Model:
[Display(Name = "Content")]
[DataType(DataType.MultilineText)]
[AllowHtml]
public virtual string NewsContent { set; get; }
When i save it to database it truncates. Well it is Text why is it truncating?
How can i fix it?
I can see in debugger that the object has all the text, when I look at database it is truncated.
Try this:
Map(x => x.NewsContent).CustomType("StringClob").CustomSqlType("nvarchar(max)")
In some NHibernate versions this truncating behavior was present due to the underlying ADO.NET having this behavior in certain circumstances. This was changed in NH 3.3 so that if the string is longer than allowed, NHibernate will throw an exception.
The above applies when using MS SQL Server as the database. If you are using this database, or still seeing this issue in this version of NHibernate, it should be regarded as bug that should be fixed. Please report it.
Related
I have a small console application which is to import a CSV file into a database. It is in .NET CORE 3.1. The CSV file gets imported without any issues. The issue arises with trying to save the data to the table. The error being received is "String or binary data would be truncated. The statement has been terminated." This is received during the context.SaveChanges() call. To determine exactly what the error is, loaded up Profiler to see the offending statement. The offending piece of code was related to a field that holds a date and time. To start from the beginning and lead up to the issue.
The imported data is in a column and is represented as follows:
"20200404121655500"
"20200404121755500"
The import model property is defined as follows:
public string Date_And_Time { get; set; }
The data model property is defined as follows:
[Column(TypeName = "DATETIME2(7)")]
public DateTime? Date_And_Time { get; set; }
The conversion used to get the imported string to the data model field is as follows:
if (DateTime.TryParseExact(Date_And_Time.Trim()
.PadRight(21, '0')
.Substring(0, 21), "yyyyMMddHHmmssFFFFFFF", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime newDateTime))
{ model.Date_And_Time = newDateTime; }
else { model.Date_And_Time = null; }
While debugging when the 2 different dates are processed then are set in the model.Date_And_Time as expected. The object expands producing all the correct information (year, day, hour, etc.). Prior to executing SaveChanges, inspecting the objects shows they both have correct DateTime objects. However upon executing that command throws the above SQL exception. When inspecting the merge command created by EF the dates are differently formatted. They are as follows:
'2020-04-0412:16:55.5000000' and '2020-04-04 12:17:55.5000000'
The first does not have a space between the date and time where as the second has a space. The first is what is causing the exception. When doing the SaveChanges there are on average 20 records being saved and some have the space and some do not there is no pattern that I can find. I have tried using the exact same date time and that also has some with and some without space.
After some trial and errors I switched from DateTime to DateTimeOffset. This was done on the property of the data model propriety, the type attribute of the same property and the TryParseException. No other changes and it ran with no exceptions. Needed to do another migration due to the change in attribute causing the table field tire change.
The only 3rd party package used was to import the CSV for and that went successfully.
The parametrized merge statement was seen in the profiler sorry I didn't City it better finding the answer.
I have no idea why the model with a C# data type of DateTime would produce a string although the parameter itself was designated as DateTime2 the value being passed in was in string format.
I don't understand what the difference is between the 2 data types and how they are handled that would cause this issue. I appreciate the comments and attempting to help.
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 have the following column on my SQL table:
RECORDTIMESTAMP
0x000000000005B2A4
0x000000000005B2A5
0x000000000005B2A6
And I need to return this values on my C# program. This atributte is declared like this:
public DateTime? RecordTimeStamp{ get; set; }
And my method to get is this one:
RecordTimeStamp = DateTime.Parse(row["RecordTimeStamp"].ToString()),
The error message is the following:
"The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.",
I also tried to declare the atribbute as string but it didn't worked also.
Any Ideas on this?
A common misconeption here: Timestamp is not actual a time.
It was a miss-naming. That is why it was renamed to ROWVERSION half a dozen versions ago. Timestamp was only still used as Alias, with a clear "deprecated" marker. From
https://learn.microsoft.com/en-us/sql/t-sql/data-types/rowversion-transact-sql:
timestamp is the synonym for the rowversion data type and is subject to the behavior of data type synonyms. In DDL statements, use rowversion instead of timestamp wherever possible. For more information, see Data Type Synonyms (Transact-SQL).
The Transact-SQL timestamp data type is different from the timestamp data type defined in the ISO standard.
Note
The timestamp syntax is deprecated. This feature will be removed in a future version of Microsoft SQL Server. Avoid using this feature in new development work, and plan to modify applications that currently use this feature.
That looks like a rowversion column (which has historically been called a "timestamp" column, despite it not being a stamp of the time). A rowversion is not a time - it is just an opaque monotonically incrementing integer that means nothing other than it lets you spot that a change has happened. Timestamps are often used for optimistic concurrency checking, but have no actual meaning. Depending on the size, a long, or a byte[] or a string might be fine for storage.
I have a .NET Web Forms app. It references WCF services, which use NHibernate, to handle data access. The backend is SQL Server.
In TableA I have a nullable column "Notes" varchar(MAX)
If I understand correctly, this column should be able to hold 2^28 characters.
I just received a call from a user today stating that her notes are getting truncated when she clicks "Save" in my application. I am able to reproduce this issue. She has entered 3960 characters into that field so far and she has 2 more sentences to add. She types them in, clicks "Save" and of the two sentences she just added, only 32 characters remain in the TextArea.
I have been stepping through my code trying to find where this data is getting truncated. I'm at a loss. I stepped through all the way to the _session.SaveOrUpdate(item); call in the Repository. I put a breakpoint on the call to SaveOrUpdate() and took a peek at the Notes property of my object. The entire string was still there. I hit F5 to continue, no exception was thrown. My notes appeared truncated in the web application. I looked at the record in the database, the Notes were truncated. They are always truncated at the same position, almost like I'm hitting a character limit.
public void Update(T item)
{
try
{
_session.BeginTransaction();
_session.SaveOrUpdate(item);
_session.Transaction.Commit();
}
catch (Exception)
{
_session.Transaction.Rollback();
throw;
}
}
I also tried:
public void Update(T item)
{
try
{
_session.BeginTransaction();
_session.Update(item);
_session.Transaction.Commit();
}
catch (Exception)
{
_session.Transaction.Rollback();
throw;
}
}
Curve ball:
In SSMS I can compose an UPDATE statement and assign the entire notes string I want to insert to the "Notes" field. After executing that UPDATE statement my entire notes string is in the "Notes" field in TableA.
Any ideas why my data is getting truncated when I insert/update via my web application?
Update:
I ran SQL Profiler and saw that my Notes string was truncated in the UPDATE statement.
I exported my NHibernate mappings so I could see what was getting generated:
<property name="Notes" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="Notes" />
</property>
Not very helpful. I'll look into the NHibernate related suggestions next.
Update Again:
Thank you icemanind and Peter Gluck for leading me in the right direction. I changed the column to nvarchar(max) and modified my NHibernate mapping
Map(x => x.Notes)
.Length(4001);
I am now able to add all of the notes, via the web app, that my user was trying to add. I need to test the resolution some more, but I'm confident enough to mark this question "Answered".
I use the following syntax to define an NVARCHAR(MAX) column when mapping with Fluent NHibernate:
const int NVarCharMax = 4001; // force NHibernate to allocate max length for nvarchar data
Map(x => x.ColumnName).Length(NVarCharMax);
This article describes the solution in more detail.
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; }