0

I have a simple Description string field, on an MVC Model which has these attributes:

[DataType(DataType.MultilineText)]
[DescriptionField]
public string Description { get; set; }

My custom [DescriptionField] simply does a regex pattern check with multiline options like this: (.|\n){0,2000}

I have some custom javascript on the view/client side which does a regex like this: ([\s\S]){0,2000}

As you can see both limit the characters to a maximum of 2000 and both the client side and server side validation work correctly so they never allow more than 2000 characters, however say if I have 1995 characters and 5 line breaks, which is a perfectly acceptable use of a multiline textbox, both client side and server side validation pass correctly and won't allow any more than that.

The underlying database field for this textbox is a sql varchar(2000 chars too) but when the Model binds the to Description field, it replaces all the line breaks with \r\n making them effectively 2 characters rather than 1, so even though the regex on client side and server side passes, when it saves to the database nHibernate says the string will be truncated because it is now 2005 characters (because of 2 characters per line break).

What is the best way around this?

User101
  • 748
  • 2
  • 10
  • 29

1 Answers1

3

In VB, this seems to work fine:

Dim strlawl As String = "Hello" & vbCrLf & "Goodbye"
Dim lenlawl As Integer = strlawl.Length // = 14
strlawl = strlawl.replace(vbCrLf, vbLf)
lenlawl = strlawl.Length // = 13

I edited the initial change to use string functions, since they are much more efficient than regex, and finding a linebreak isn't exactly a "pattern". Though, with the string method, you wont be replacing multiple line-breaks with one, as you would be doing if you used 'regex.replace(strlawl, "[\r\n]+", vbLf)'

However, If you find the requirement to have both the line break and carriage return when you bring it back out, you'll want to use regex, because the inverse of the above string function would be very unstable. So:

strlawl = System.Text.RegularExpressions.Regex.Replace(strlawl, "[\r\n]+", Environment.Newline)

Yes, I do recommend keeping both the \r and \n in there like that for backward compatibility. On the other hand, it is more than likely not even necessary for you to re-convert. Line Feed should cause a break without the Carriage Return. But hey, clean code is clean.

Breakdown:

[       // Begins a new Character Class
    \r  // Allow matches for Carriage Return
    \n  // Also allow matches for Line Feed
]+      // Require at least one, but match as many as possible

Doing a regex replace is a bit heavy. It isn't a real concern, but if your replacement doesn't require pattern matching, it's a no-brainer to use string replacement. That being said, the Replace function is very efficient and isn't messy.

With the nature of mutliline textbox (or RTB's) being what they are, you're going to have \r\n in your return, and the only way I know to get rid of them is to replace them with something. My initial thought to answer the question was to replace them with a Pipe, then change them back when outputting, but I don't know what all of your textboxes accept or their purpose, so I went the safe route with the above response.

That being said, another option might be:

strlawl.replace(vbCr, Nothing)

But still, if you want both the CR and LF in your returned value, you'd be safest to use Regex to find patterns of any number or combination of consecutive CR's and/or LF's, and just properly replacing them with CRLF or NewLine.

All THAT being said, you say you have multiple pages and multiple text areas. Your web pages (codebehind) should not have any connections to your database. They should be using a data layer. A seperate DLL either in the project, or accessed via a Web Service. At a minimum, within that data layer you could have a shared method to reduce or reincorporate line breaks as you please. Then txtBox.Text = fix(strValue). You'd do this in the txtFieldName.DataBound event.

ASP Codebehind:

Private Sub txtFieldName_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtFieldName.DataBound
    txtFieldName.Text = fix(strValue)
End Sub

Your question is about MVC Though, which I'm not quite pro at. But this is a link that would help. You could implement my answer here using tips from: Custom Model

Yeah, might be a bummer to have to update lots of text fields for one bug found. But that's the nature of the beast. In all honesty, I do hope somebody has another solution. But bugs are what they are.

Community
  • 1
  • 1
Suamere
  • 5,691
  • 2
  • 44
  • 58
  • I guess this is a potential solution but isn't it a bit messy doing a Replace? Also I have description fields across the site so I would need to do this in many places, is it possible to use my custom attribute and change its value automatically when the model binds using this replace? – User101 Jun 10 '13 at 19:40
  • You're right about one thing, I took your relationship with regex (and my bias) and went that route. It would certainly be better to not use regex at all. Regex is 10x slower than string functions. I am editing my code now, and will answer your commented concern in a moment. – Suamere Jun 10 '13 at 20:01
  • Hey @Suamere thanks for your response, I think we're both along the right lines, but as I am using MVC I can't do txtFieldName_DataBound but I can set that [DescriptionField] attribute on all my models that use it, but I would need to work out how to put this \r\n replace into my [DescriptionField] field if that makes sense? – User101 Jun 11 '13 at 10:07
  • 1
    I'm not an MVC Pro, but maybe this will help?: [CustomModel](http://stackoverflow.com/questions/10946575/how-can-edit-the-value-of-html-textboxfor-in-controller-before-use-it) – Suamere Jun 11 '13 at 16:13
  • Yes custom model binding is what I'm looking for to do this across the site! Thanks! – User101 Jun 11 '13 at 19:32
  • 1
    Wow why does this answer not have more upvotes? The definitive overview of line breaks and carriage returns. – Shawn J. Molloy Jan 14 '14 at 01:39