我有以下功能:
public void rpSearchResults_ItemDataBound(Object Sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
System.Data.Common.DbDataRecord rd = (System.Data.Common.DbDataRecord)e.Item.DataItem;
Literal litCompanyName = (Literal)e.Item.FindControl("litCompanyName");
Literal litCompanyLocation = (Literal)e.Item.FindControl("litCompanyLocation");
Literal litCompanyActivity = (Literal)e.Item.FindControl("litCompanyActivity");
HiddenField hdnUserID = (HiddenField)e.Item.FindControl("hdnUserID");
HyperLink lnkEmail = (HyperLink)e.Item.FindControl("lnkEmail");
HyperLink lnkMicrosite = (HyperLink)e.Item.FindControl("lnkMicrosite");
Literal litRecommends = (Literal)e.Item.FindControl("litRecommends");
litCompanyName.Text = rd["CompanyName"].ToString();
// Construct location info
litCompanyLocation.Text = "";
string[] addressParts = {"City","Region","Postcode"};
bool prior = false;
foreach (String part in addressParts){
if (prior) litCompanyLocation.Text = litCompanyLocation.Text + ", ";
string addressBit = rd[part].ToString();
if (addressBit == null || addressBit.Trim() == "") prior = false;
else
{
litCompanyLocation.Text = litCompanyLocation.Text + rd[part].ToString();
prior = true;
}
}
// ... and so on, mapping stuff from the database to fields.
}
}
它由我的Repeater上的事件触发,用于填充搜索结果列表。找到的所有各种控件都存在于转发器的项模板中。
这一切都有效,但我不想这样做。我讨厌必须通过字符串搜索控件,并且我讨厌必须进行强制转换以使控件进入我可以操作的状态。如果出现任何问题 - 例如,如果我输错了 - 错误只在运行时被选中,并且通常很难跟踪,因为这些函数似乎无声地失败,而不是创建一个明显的错误页面
此代码现已编写,并且可以正常运行。但是在将来我想用一种强类型的不同方式编写它,这样任何错误都会在编译时让它们自己知道。有没有办法做到这一点?
答案 0 :(得分:2)
您可以将所有控件放入强类型自定义控件(包括ASCX用户控件)中,只需使用FindControl()
一次:
CustomControl myControl= (CustomControl) e.Item.FindControl("oMyControl");
myControl.litCompanyName.Text = rd["CompanyName"].ToString();
答案 1 :(得分:2)
注意:根据您在示例中发布的内容,您使用<%#%>复制Repeater类的现有数据绑定功能。句法。我很长时间没有使用ASP.NET数据绑定,所以假设你实际上有充分的理由做很长的事情,这里是你实际问题的答案:
不幸的是,没有任何简单且强类型的方法来实现你想要的,至少不是在“旧”(非MVC - 它有名字吗?)ASP.NET框架。 Repeater控件包含一个Control集合,它只知道其中的每个对象都派生自System.Web.UI.Control
。更具体地说,如果您的代码和ItemTemplate不同步,则需要在运行时进行类型转换,这可能会失败。
有一些选项可以让问题减轻痛苦,每个问题都需要权衡取舍:
保持原样。您的代码有效,您已经知道了难点,并且您已经知道如何避免它们。实用软件开发的首要规则之一是永不破坏工作代码。
将ItemTemplate中的所有控件包装成强类型的内容,例如ASP.NET用户控件。这非常接近于完成你想要的东西:你只有一次机会搞乱FindControl
或类型转换,一旦你处理了你的类型属性。缺点是将数据源绑定到ASCX上控件属性所涉及的额外工作。如果项目模板不经常更改,这可能是您的最佳选择。
通过检查您的类型转换是否真的有效,您可以获得更好的运行时错误检测。例如:
Literal litCompanyName = e.Item.FindControl("litCompanyName") as Literal;
if ( listCompanyName == null )
LogSomeError("litCompanyName", "Literal");
显然,缺点是输入和维护的代码更多,但是如果你真的发现你的运行时错误被静默吞噬,那么从长远来看这可能会节省你的时间。此外,它还可以让您更好地从错误的类型转换中恢复 由于异常而没有强制整个方法保释。
避免使用ASP.NET Repeater(或任何其他基于ItemTemplate的控件)。这是对未来产品的更多建议,因为我的第一个建议与@Henk的评论相似 - 使用MVC。 MVC框架的好处很多,强类型模型绑定就是其中之一。它没有的是ASP.NET拥有的现有控件库 - 您需要使用例如部分视图来创建类似转发器的控件。这项工作更多,但从长远来看,很多更容易维护。
答案 2 :(得分:1)
不。这是您需要这样做的方式。您至少在定义中转换为正确的类型,因此您可以使用要键入的对象。
问题是访问hte控件的唯一方法是使用FindControl方法。这就是问题的核心所在 - 如果可以用类型定义的查找替换它会更好。
答案 3 :(得分:1)
不幸的是,没有办法解决。你可以做的最多是创建枚举或常量以传入findcontrol。你需要做的铸造,这对容器控制来说有点烦人。
答案 4 :(得分:1)
你不需要在代码中执行此操作...没关系,但你必须输入字段名两次(一次创建控件,一次为其赋值),这会增加你的错别字的机会。
您可以在转发器的标记中进行分配。 E.g:
<ItemTemplate>
<tr>
<td><%#Container.DataItem("title")%></td>
...
它仍然存在运行时漏洞,但至少您只需要这样输入字段名称。
编辑 - 根据您对3个字段进行组合的评论...这也是在存储过程中完成这项工作的选项......
SELECT
CAST(address as varchar(100)) +
CASE WHEN NOT NULLIF(address,'') is null THEN ', ' ELSE '' END +
CAST (region as varchar(100)) +
CASE WHEN NOT NULLIF(region,'') is null THEN ', ' ELSE '' END +
CAST (postcode as varchar(100) as CompanyLocation,
...
FROM
...