Problem with Substring() - ArgumentOutOfRangeException
I have a repeater that displays data from my Projects table. There are projectId, name and description. I use Substring(1, 240) on descrip开发者_运维技巧tion. But sometimes the string is shorter than 240, so I get ArgumentOutOfRangeException. Can you tell me how to display the whole text if I get the exception. This is my code.
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<asp:Panel ID="pnlDisplayProjects" runat="server" Visible="true">
<center><h2><b>Проекти</b></h2></center>
<asp:Repeater ID="rptrProjects" runat="server">
<HeaderTemplate>
<table border="1" cellpadding="2" cellspacing="2" align="center" width="80%" style="background-color:#F7F6F3;">
</HeaderTemplate>
<ItemTemplate>
<tr>
<td align="left" style="width:40px">
<asp:Label ID="LblProjectId" runat="server" Text='<%# Eval("ProjectID") %>' />
</td>
<td align="center">
<asp:Label ID="LblName" runat="server" Text='<%# Eval("Name") %>' />
</td>
</tr>
<tr>
<td colspan="2">
<asp:Label ID="LblDescription" runat="server" Text='<%# Eval("Description").ToString().Substring(1, 240) + "..." %>'/>
<asp:HyperLink ID="HlMore" runat="server" NavigateUrl='<%#"~/Project/ViewProject.aspx?projectId=" + Eval("ProjectID") %>' Text="More" />
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
</asp:Panel>
protected override void OnPreRender(EventArgs e)
{
var table = Projects.GetTableWithProjects();
if (table.Rows.Count > 0)
{
rptrProjects.DataSource = table;
rptrProjects.DataBind();
}
else
{
pnlDisplayProjects.Visible = false;
Master.PrintMessage("There are no projects.");
}
}
I would suggest you write a separate extension method if you're using .NET 3.5. Something like this:
public static string SafeSubstring(this string text, int start, int length)
{
return text.Length <= start ? ""
: text.Length - start <= length ? text.Substring(start)
: text.Substring(start, length);
}
Basically that's a version of Substring
which will never throw an exception unless start or length is negative (in which case I don't know what it could sensibly return).
You'd call it like this:
Eval("Description").ToString().SafeSubstring(1, 240) + "..."
The downside of this is that it will include the ellipsis (...) even if it's not actually truncated the string at all...
string dec = "description";
string result = dec.Substring( 0, dec.Length > 240 ? 240 : dec.Length )
description.Substring(0, Math.Min(description.Length, 240));
Based on Jon Skeet's answer, I think it should be checking for null or else it's not exactly a safe method :)
public static string SafeSubstring(this string text, int start, int length)
{
if (text == null) return null;
return text.Length <= start ? ""
: text.Length - start <= length ? text.Substring(start)
: text.Substring(start, length);
}
An extension method:
public static string SafeSubstring(this string text, int start, int length)
{
if (start >= text.Length)
return "";
if (start + length > text.Length)
length = text.Length - start;
return text.Substring(start, length);
}
You can use LINQ as below:
string someString = "abcde";
string subStr = string.Join("", someString.Take(240));
It's a bit of a hack, but the simplest method would be to change:
Eval("Description").ToString().Substring(1,240)
to
Eval("Description").ToString().PadRight(240).Substring(1, 240)
I'm not sure about the performance considerations on this, though, if therre are a lot of items.
If you REQUIRE 240 characters in that place for some reason, you can cast that field coming out of the database as a char(241)
in which case it will always be 241 characters, with padding to the right if the content of the field was shorter than 241 characters. Then you can still strip off the first character with the Substring(1, 240)
as you are right now.
Although, I must wonder if you're not wanting to do a Substring(0, 240)
which wouldn't throw an exception if the string was shorter than 240 characters, and would start at the beginning of the string, rather than the 2nd character.
Another option is to write a a method that you call so your eval turns into:
<%# GetString(Container.DataItem) %>
So in the method you could check to make sure that the item is NOT null because you may bomb out calling .ToString on a NULL item. Then you can evaluate the container item and pass in the length to the substring call.
Text='<%# Eval("Description").ToString().Substring(1, Math.Min(240, Eval("Description").ToString().Length - 1)) + "..." %>'
Let's try to keep this simple...
We only need to truncate to a given max length, so how about we call it what it is:
description.TruncateTo(240);
The extension method that enables the above (ellipsis is appended by default if truncated):
public static class StringExtensions
{
public static string TruncateTo(this string val, int maxLength, bool ellipsis = true)
{
if (val == null || val.Length <= maxLength)
{
return val;
}
ellipsis = ellipsis && maxLength >= 3;
return ellipsis ? val.Substring(0, maxLength - 3) + "..." : val.Substring(0, maxLength);
}
}
Using .Net 5 or higher.
I recommend using:
yourText.Substring(0, Math.Min(yourText.Length, 100));
This logic will use the smallest value between the comparisons, as it returns the smallest of two numbers. As per MS documentation.
精彩评论