15

I am getting a DbUpdateException with message

String or binary data would be truncated

I understand that one of the fields in the entity won't fit the length of the column in the database. And that I could go down and check them manually.

What I am trying to do however, is get a sensible error message which might tell me which field it actually is! E.g.

String or binary would be truncated at field ProspectName.

I am able to print out a lot of random information. and have tried various stuff. But nothing points to the field name.

Please note this is NOT of type DbEntityValidationException, it is a DbUpdateException

// DbUpdateException exception
foreach (var entry in exception.Entries)
{ 
    builder.AppendLine(String.Format("Error at: Type {0}", entry.Entity.GetType().Name));

    if ((exception.InnerException is System.Data.Entity.Core.UpdateException) &&
        (exception.InnerException.InnerException is System.Data.SqlClient.SqlException))
    {
        var updateException = (System.Data.Entity.Core.UpdateException)exception.InnerException;

        var sqlException = (System.Data.SqlClient.SqlException)exception.InnerException.InnerException;
        var result = new List<ValidationResult>();

        for (int i = 0; i < sqlException.Errors.Count; i++)
        {
            builder.AppendLine(String.Format("Error code: {0} ", sqlException.Errors[i].Number));
            builder.AppendLine(String.Format("Source: {0} ", sqlException.Errors[i].Source));
            builder.AppendLine(String.Format("Message: {0} ", sqlException.Errors[i].Message));
            builder.AppendLine(String.Format("State: {0} ", sqlException.Errors[i].State));
            builder.AppendLine(String.Format("Procedure: {0} ", sqlException.Errors[i].Procedure));
        } 
    }
}

Complete error:

String or binary data would be truncated. The statement has been terminated.

Error at: Type tree_1ECACDBB4458C7A9DEC7CD183FD8B8C3473502FEFFACF160E17AD47718DCE5EA
Error code: 8152
Source: .Net SqlClient Data Provider
Message: String or binary data would be truncated.
State: 14
Procedure:
Error code: 3621
Source: .Net SqlClient Data Provider
Message: The statement has been terminated.
State: 0
Procedure:

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
IAmGroot
  • 13,760
  • 18
  • 84
  • 154
  • You could consider temporarily having SET ANSI_WARNINGS OFF. Then run the query and inspect what was written to the database, and you may be able to visually identify the offending column. – Joe Apr 13 '17 at 18:01
  • @Joe How would I programmatically get it to tell me which field though? I understand i can manually check. But we have many old db deployments that have varied field lengths. And it would be nice if i could be told by the program which field, since it knows one is overflowing. – IAmGroot Apr 14 '17 at 09:05
  • This is happening when you are running update-database? Is it when you are seeding the data in the database? – trees_are_great Apr 24 '17 at 09:29
  • @Sam I get this error, trying to update an entity in the database. No doubt, one of the fields is set too long. But how would you get it to report in the error, which field. – IAmGroot Apr 24 '17 at 09:31
  • 1
    This is a duplicate? http://stackoverflow.com/questions/779082/sqlexception-string-or-binary-data-would-be-truncated – trees_are_great Apr 24 '17 at 09:45
  • @Sam It looks similar yes. But he doesn't ask specifically for a programmatic response. I am aware (and have) profiled the server to find the query and field that caused it. But what i want is a better error message that points to the field in question. – IAmGroot Apr 24 '17 at 10:05
  • alright, I'm guessing the answer is that it doesn't exist, but who am I to say. Good luck with your quest. – trees_are_great Apr 24 '17 at 10:09
  • You cannot do that, just because sql server itself does not give away this information (and that is sql server error, not related to EF). Here is a very old bug in their tracker about that: https://connect.microsoft.com/SQLServer/feedback/details/339410/please-fix-the-string-or-binary-data-would-be-truncated-message-to-give-the-column-name. If you scroll a bit you will see that on 05.08.2016 Microsoft claims there are some minor efforts to fix this, without any estimation. – Evk Apr 24 '17 at 10:17
  • @Evk. That's what i started to wonder. What about validating the model for field lengths before saving? Say if i set a string to char 3, when the entity specifies char 2. (WCF). Is there any suggested route I should look at? Its a DB first Entity model. Would I have to create partial classes with meta data tags on length checks? – IAmGroot Apr 24 '17 at 10:22
  • Well it should not get to this point if you have proper EF model with correct length for the fields (for example by decorating with Length attributes) and database model is relatively simple (without triggers for example). In that case EF will throw validation exception before executing any statements. Maybe you should post your EF model for the entity you have this error with. – Evk Apr 24 '17 at 10:54
  • @Evk Its just generated a partial class. The field in the class is declared as `string tree_type { get; set; }` No attributes on it. in the EDMX though, it has the property `Max Length = 2`. However, I managed to put a string of length 3 in and attempt to save it, through to the sql server. Its database first driven. If i add a length tag to a partial class, it wouldnt update if the db length changed and edmx refreshed. Maybe im going about it wrong – IAmGroot Apr 24 '17 at 11:02
  • 1
    Max length should be respected and throw validation error. Maybe it's not that column which causes problem? Maybe you have some triggers? Hard to tell given the information you provided. You will have to debug this as usual (remove code until you get minimal code to reproduce it). – Evk Apr 24 '17 at 12:22
  • @Doomsknight I didn't read your question carefully enough so deleted my answer. However I'm pretty sure a DbUpdateException.InnerException will contain a DbEntityValidationException. I can't force an DbUpdateException for some reason (I just get DbEntityValidationException) to test that out. – Phil Apr 26 '17 at 16:45
  • @Phil Ye, i was reading through it. And wondering if I should even be getting `DBUpdateException`, and that `DBEntityValidationException` should be thrown long before. Im beginning to wonder maybe there is an issue with the meta data. I looped through the entity, posting out max field lengths, and for the table in the entity that i care about, it didnt print anything as if it didnt exist. My question doesn't address this though. The inner exception is of type `SqlException` and seems to contain nothing useful. – IAmGroot Apr 27 '17 at 08:14
  • Does this help? https://stackoverflow.com/a/71329954/8644294 – Ash K Dec 21 '22 at 17:01

4 Answers4

7

One "ugly" solution (but functional and using ONLY C# code) to find exactly which property is giving you that error would be:

In case you are doing an update do this:

 var myDBObj = db.Mytables.Where(x=>x.Id == myId).FirstOrDefaul();
 if(myDBObj == null) return false; // or something else with the error msg

 myDBObj.Property1 = myObjToSave.Property1;
 db.SaveChanges();

 myDBObj.Property2 = myObjToSave.Property2;
 db.SaveChanges();

 myDBObj.Property3 = myObjToSave.Property3;
 db.SaveChanges();
 .... // EACH PROPERTY....
 myDBObj.PropertyX = myObjToSave.PropertyX;
 db.SaveChanges();

Now, with a brake point or an incremental variable to track the "position", etc... you will know exactly in this specific case where you have the exception....

NOTE: this is an ugly solution JUST to track down where it is failling... NEVER use this in production... and of course IMO there are other more friendly things like having a sql profiler and see the generated SQL and then try to run it on the sql management studio and then see the error there......

UPDATE #1

Something like this (note: I did not compile it) :P

Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();  
string lastPropertyWithError = ""; // You can replace this with a list or so  
foreach (PropertyInfo property in properties)
{
   try{
    property.SetValue(myDBObj, property.GetValue(myObj, null));
    db.SaveChanges();
   }catch()
   {
     lastPropertyWithError  = property.Name;
   }
}
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
Dryadwoods
  • 2,875
  • 5
  • 42
  • 72
  • 1
    As you said, its not a great solution. And would require a lot of coding for each enttity. But it does answer the question. (And is the only answer that does) +1 – IAmGroot Apr 28 '17 at 08:31
  • @Doomsknight yep, and I am sure that I could code you something to do this automatically for you (with reflection or so) :) But I think this is not what you are looking for. – Dryadwoods Apr 28 '17 at 08:33
  • Going to assume that there is no ideal answer, and that I think there is an issue with the meta data, as it should catch before attempting a save. But that since this is the best given the situation, I have awarded it the bounty / correct answer to close the question. – IAmGroot Apr 28 '17 at 08:40
  • 1
    Thanks for the accepted answer, I will also give you some reflection code: wait some seconds :P – Dryadwoods Apr 28 '17 at 08:40
  • 1
    Done, I think you get the point.... I just dont know if this will work :P in theory it should :P – Dryadwoods Apr 28 '17 at 08:44
  • NOTE, I am not sure if ALL the properties would be a "correct" property to GET/SET value... ... with dataannotations it would be easier to identify which ones to apply the logic. – Dryadwoods Apr 28 '17 at 08:46
  • I like it. Thanks. Could probably build a test with this kind of code. That could be run when something is going wrong. – IAmGroot Apr 28 '17 at 08:47
  • There's a better way of doing this by comparing column widths directly from the database table to the widths of values we're trying to insert. Take a look at my answer below: https://stackoverflow.com/a/71341426/8644294 – Ash K Mar 03 '22 at 17:41
2

Using Entity framework profiler to capture the sql queries from the entity framework, find your update query and run it against the database directly, you have good chances of finding the field.

Shaik
  • 394
  • 2
  • 3
  • 11
0

ProspectName is the field that could be truncated. I don't know how that is mapped to your database, though. You could look that up in FluentApi or DataAnnotation depending on how you set it up.

You'll need to see what the MaxLength is set for this field and make sure it matches with your database max length. If it does, then you'll need to make sure you don't pass a too long of a string on this field. It could also be that the data type doesn't match between the entity and database, so check that too.

Right now, I just don't have a lot of other information to help you further.

Daniel Lorenz
  • 4,178
  • 1
  • 32
  • 39
  • Its a database first, EDMX deployment. How would i pro grammatically find which field was the issue. When this error shows. – IAmGroot Apr 14 '17 at 09:06
  • I would right click on the entity class and Go To Definition. There, you would see ProspectName. Right click on that Find All References. You should see somewhere where this is used in Property() or something. Go there and you should see what the max length it is using. If your code lets a longer string than that get set on that property, then you will get this error every time. – Daniel Lorenz Apr 14 '17 at 12:19
0

Following on from @Shaik:

public MyDbContext(string connectionString, ILogger<PeopleHrDbContext> logger)
    : base(connectionString)
{
    _logger = logger;
    Database.Log = logger.Debug;
}

I am using NLog and Ioc, so you can pass a logger to your ef context and in your configured log output, you can see what the generated queries looks like. For me that is the debug output window. I have pasted in below my NLog set-up to support this.

  <targets>
    <target xsi:type="Debugger" name="d" 
            layout="${longdate} ${logger} ${uppercase:${level}} ${message}" />
  </targets>

  <rules>
    <logger name="*" minlevel="Trace" writeTo="d" />
  </rules>
IbrarMumtaz
  • 4,235
  • 7
  • 44
  • 63
  • I've got that, and it doesn't help you to find the DB column. – Auspex May 08 '19 at 15:16
  • I think the idea is print out your sql queries to the output window and then manuall copy and paste into SSMS and run it locally to see what is going wrong with the sql. – IbrarMumtaz May 09 '19 at 09:18
  • Well, I had the query, and I had the values. Since it was EF core, it was batching the updates in random quantities (well, I presume not _random_, but an algorithm that's opaque to the programmer), and so all I could tell was that it was in one of 83 rows. Since it wasn't the first row, I know it wasn't _all_ rows. Sure, I could have narrowed it down with a binary search in no more than 9 steps. Since I didn't actually _need_ all 25 columns, I narrowed _those_ down with a binary search, and fortunately found I didn't have to have the problem column. It's still tedious & should be unnecessary.. – Auspex May 10 '19 at 09:36
  • @Auspex - sorry dude - dont have much experience with efcore. My post for anyone needing help in EF6 debugging column issues. It automagically prints out sql queries to defined logger in the background. – IbrarMumtaz May 13 '19 at 10:29