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.
Related
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 want to get the last inserted id returned when inserting in my visual C# program.
// Insert to database
try
{
int result = int.Parse(tblClipManagerTableAdapter.InsertQuery(textBox2.Text, textBox1.Text).ToString());
MessageBox.Show(result.ToString());
}
catch (Exception ex) {
MessageBox.Show("Fejl: " + ex.Message.ToString());
}
Error is: "Object reference not set to an instance of an object."
I have set the tblClipManagerTableAdapter.InsertQuery execute mode to scalar.
Any help on how to return the last inserted ID with my current code and setup?
Using a local SQL database (dbClipManager.sdf).
Best regards
EDIT
I think I found something that I could use.
But I am very new to this. Not sure how to complety write the code?
Im used to just use the datasets, not opening connections, execute SQL cmds. :o/
http://social.msdn.microsoft.com/Forums/en-US/sqlce/thread/1d1d3267-dc29-470b-bb20-00487a39bc87/
Best regards
There are several problems with your approach, so I'd suggest you do a quick read through of table adapter and make sure that you've got the basics down.
If you're trying to add a new record, you should be calling TableAdapter.Insert, not TableAdapter.Update
You can't create a second command to the database and use ##IDENTITY to get back the ID that was previously generated... this has to be returned within the same session, otherwise SQL would have no idea which ID you are expecting to receive.
If you set the execute mode of the table adapter to scalar, then the ID will be the return value of the method call. Please see this Question and Answer on the same issue that you're experiencing.
EDIT:
Please see this walk-through for what you're trying to accomplish.
New with EDS. VWD 2011.
Adding vehicles to my vehicle file. PK is Company number (char 5) and vehicle code (char 15). Using a details view with only the main keys and the entitydatasource is defined and working with datakeynames.
In the EDS inserting event, i want to go into the file, and see if what is entered, is there first. It adds perfect if a new code, and of course bombs is duplicate.
Old visual foxpro programmer, just learning this. The EDS is already opened with the correct file in the database and everything.. maybe if I do a count() "where" condition, and if 0, maybe new... I really just want some foxpro SEEK COMPANYNUMBER+VEHCODE and if not there, allow to add...
Thanks for any input. It is 2:19am in the morning.. fun teaching myself something new...lots of hours..
Frank C :)
You 'could' go out and check to see if the record already exists, and than add it only if it does not; perhaps a better way is to have a unique index on that field combination so that it would be impossible to save duplicate records, and then let EF tell you when the save changes has failed. This will prevent you from making two calls to the DB on every insert attempt.
Example:
try
{
if (ModelState.IsValid)
{
db.Vehicles.Add(vehicle);
db.SaveChanges();
}
}
catch (DataException)
{
//Log the error (add a variable name after DataException)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
I have a list view with two colums in wpf Customername and Isvalid.I am using linq to sql to get the data from my sql table.when i am trying to update a value to the table i dont see any changes to the table.
Here is my code when i click on the save button:
try
{
CustomersDataContext dataContext = new CustomersDataContext();
Customer customerRow = MyDataGrid.SelectedItem as Customer;
string m = customerRow.CustomerName;
Customer customer = (from p in dataContext.Customers
where p.CustomerName == customerRow.CustomerName
select p).Single();
customer.Isvalid=false;
dataContext.SubmitChanges();
MessageBox.Show("Row Updated Successfully.");
}
catch (Exception Ex)
{
MessageBox.Show(Ex.Message);
return;
}
I can see that i am able to query the record based on the customername selected but the value is not updating.
I would be glad if some one can point out where am i missing the logic to update the "ISVALID" value to the data base.
Firstly, where's your using(get_datacontext){...} block? You need to dispose of DataContexts when you are done with them!
Anyway...
My guess is that the update statement is generating a where clause that's far too tight, or just plain wrong.
I would be checking the 'Update Check' property of each of the columns in your mapped table in the Linq to Sql designer. The simplest thing to do is to set the primary key column to Always and set all the others to Never. You can also consider setting them to WhenChanged.
The designer's default behaviour is generally to set it to Always for everything; not only does this cause horrible WHERE clauses for updates, but can occasionally also cause problems. Obviously such behaviour is required for proper concurrency checking (i.e. two threads updating the same row); so be aware of that.
Oh, thinking of something else - you can also get this behaviour if your table doesn't have a primary key designated in the designer - make sure one of the columns is.
Finally you can check the SQL being generated when SubmitChanges is called; by attaching a TextWriter to the DataContext.Log property; or equally IntelliTrace in VS2010 will collect all ADO.Net queries that are run if you start with debugging. This is invaluable in debugging why L2S stuff isn't working as expected.
You should Add updated Customer to list of updating customers. I mean before saving changes you should do something like: db.AddToCustomers(customer). AddToCustomers in used in EF, I exactly don't know its equivalent in LINQ.
First of all, there is no chance that this is a multi-user issue, as I'm working locally on a dev version of the database.
I am getting the not very explanatory Row not found or changed error being thrown when I perform db.SubmitChanges(). If I break the execution just before the SubmitChanges() occurs, I can check in SQL Server Management Studio and the row does exist!
Here's the code for the whole function, just to put it in context for anyone who wants to help, but the problem line is right at the end (line 48).
Update This is a really odd one: the error is caused by updating matchingTrans.Url (see penultimate line of code). Commenting out this line doesn't throw the error - even if the matchingTrans.Title still gets updated.
private static void MenuItemUpdate(int languageId, NavigationItem item)
{
using (var db = DataContextFactory.Create<MyDataContext>())
{
// Select existing menu item from database.
var dbItem =
(from i in db.MenuItems
where i.Id == item.Id
select i).Single();
// Obtain ID of link type.
dbItem.FkLinkTypeId = GetLinkTypeByName(
Enum.GetName(typeof (NavigationItemLinkType), item.LinkType)).Id;
// Update the Link field with what is given.
dbItem.Link = item.Link;
db.SubmitChanges();
// Item already exists and needs editing.
// Get associated translations.
var trans =
from t in db.MenuItemTranslations
where t.FkMenuItemId == item.Id
select t;
// If translation exists for given language, edit it.
var matchingTrans =
(from t in trans
where t.FkLanguageId == languageId
select t).SingleOrDefault();
if (matchingTrans == null)
{
// No matching translation - add one.
var newDbTrans = new MenuItemTranslation
{
FkMenuItemId = item.Id,
FkLanguageId = languageId,
Title = item.Title,
Url = item.FriendlyUrl
};
db.MenuItemTranslations.InsertOnSubmit(newDbTrans);
db.SubmitChanges();
}
else
{
// Matching translation - edit it.
matchingTrans.Title = item.Title;
matchingTrans.Url = item.FriendlyUrl;
db.SubmitChanges();
// WTF ERROR: Row not found or changed.
}
}
}
Looking at the SQL Profiler output, it helped me figure out the answer to this. There was a bad piece of SQL being generated which ended with WHERE 0 = 1 ... an obvious error.
It turns out that the field had simply been changed to allow nulls by another developer, and the Linq-to-SQL file hadn't been updated accordingly.
In short, if the Row not found or changed error message appears to be generated for no reason, make sure your database schema exactly matches your .dbml file else you'll get this error message on any fields that have slightly differing schemas.
Take a look at the connection property "No Count" at sql server server level
1. Right click on Sql server connection in Object Explorer -->Property
2. Go to Connection Tab/Page
3. Look for the Default connection option "no count"
4. Make sure this option is not checked.
Another possibility that I've found to add to the excellent list of answers here:
When using a not-nullable column in a database - then mapping that to a datatype that is intrinsically nullable (in this example DB type is LONG BLOB NOT NULL mapped to a byte array in c#) you can end up in a situation where updating the database with the exact same byte array causes this error to be thrown.
Example: You have a website that allows the user to upload an image to the database. Your table has a blob (image in sql server, whatever) that is not nullable. The user chooses to update the record with the exact same image that is already there. The update check will fail. I fixed this by first doing a .SequenceEqual() check and then only calling .SubmitChanges() on the context object if the incoming byte array was not equal to the existing one.
I had this issue even when the database schema and dbml matched exactly. The issue was I was trying to change an entity and insert entities in a single SubmitChanges statement. I fixed it by doing SubmitChanges on each operation instead of all at once.
This was all in a transaction scope so that may have something to do with it but I'm not sure.