背景
我们知道如果两个表之间有外键关系,Dynamic data framework会为相应的字段添加下拉列表来选择相应的值,提供了友好的操作方式。
但是在实际应用中我们通常会有些数据字典或者松散的关联关系,比如
这个图中,创建人就是来自用户表中。关系如下(两个关联的字段类型都是Guid):
但二者并没有建立显式的外键关系。
我们希望在aspnet_module 在显示时,显示友好的名称,而不是一串Guid,在编辑的界面能够下拉选择。如下图:
解决方案
解决这个问题有两个方面:
-
(1) 定制界面;
-
(2) 获取数据,包括静态界面数据和下拉列表中的数据;
在.net Framework3.5 SP1中,提供了强大的Metadata动态绑定方法,这是Dynamic data framework基础之一。网上已经有大量的资料来描述。值得一提的是UIHint这个属性,它为我们定制显示提供了很好的扩展能力。
[DisplayName("创建人")]
[UIHint("SamEnumeration", null, "TableName", "aspnet_Users","LookupField","UserId","ResultField","UserName")]
public Guid CreatedBy { get; set; }
我希望这个UIHint传入了参数
{TableName=” aspnet_Users”, LookupField ="UserId", ResultField="UserName"}
其含义就是 CreatedBy这个字段是来自于aspnet_Users的UserId字段,显示时请利用对应记录的UserName字段。
我们需要定制两个ascx文件对对应的静态界面和编辑界面进行Render。
对于静态的页面SamEnumeration.ascx如何获取数据呢?
关键我利用了Linq中的ExecuteQuery方法,当然我们也可以利用Dynamic Expression表达式。关键就是拼一个sql让ExecuteQuery去执行,代码如下:
Code
1 public static string GetLookupValue(string tableName, string lookupField, string resultField, string whereCondition, string FieldValueString)
2 {
3 string ret = string.Empty;
4
5 //ret=(new MRPTablesDataContext()).aspnet_Users.Where(s => s.UserId.ToString() == FieldValueString).Select(s => s.UserName).First();
6
7 if (!string.IsNullOrEmpty(FieldValueString))
8 {
9
10 string sql = string.Format("select {0} from {1} where {2}='{3}' and {4}", resultField, tableName, lookupField, FieldValueString,
11 string.IsNullOrEmpty(whereCondition) ? "1=1" : whereCondition);
12
13 ret = new MRPTablesDataContext().ExecuteQuery<string>(sql).First();
14 }
15 return ret;
16 }
我增加了一个whereCondition来支持筛选一些记录。
这样我们就可以直接在SamEnumeration.ascx将GetLookupValue的结果显示在页面上。前端代码很简单:
Code
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SamEnumeration.ascx.cs" Inherits="SmallMRPV3.DynamicData.FieldTemplates.SamEnumeration" %>
<asp:Literal runat="server" ID="Literal1" Text="<%# getFieldValueString() %>" />
Code Behind:
Code
public partial class SamEnumeration :FieldTemplateUserControl
{
public override Control DataControl
{
get
{
return Literal1;
}
}
protected string getFieldValueString()
{
string tableName = GetUIHintArg("TableName");
string lookupField = GetUIHintArg("LookupField");
string resultField = GetUIHintArg("ResultField");
string whereCondition = GetUIHintArg("WhereCondition");
string value = SamDBHelper.GetLookupValue(tableName, lookupField, resultField, whereCondition, FieldValueString);
return value;
}
}
这里有一个技巧如何取得UIHint传过来的参数,请看代码:
Code
protected string GetUIHintArg(string key)
{
UIHintAttribute hint = null;
string returnValue = string.Empty;
hint = (UIHintAttribute)this.Column.Attributes[typeof(UIHintAttribute)];
if (hint != null)
{
if (hint.ControlParameters.ContainsKey(key))
{
returnValue = hint.ControlParameters[key].ToString();
}
}
return returnValue;
}
同理我们定制SamEnumeration_Edit.ascx,这个比较复杂的地方在如何将数据绑定到dropdownlist中。前端代码依然很简单:
Code
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="SamEnumeration_Edit.ascx.cs" Inherits="SmallMRPV3.DynamicData.FieldTemplates.SamEnumeration_Edit" %>
<asp:DropDownList ID="DropDownList1" runat="server" CssClass="droplist">
</asp:DropDownList>
我定义了一个类来存放用于绑定的数据:
Code
public class SamLookupData
{
public string SAM_TextField { get; set; }
public object SAM_ValueField { get;set;}
public SamLookupData() { }
public SamLookupData(string textFieldData, object valueFieldData)
{
SAM_TextField = textFieldData;
SAM_ValueField = valueFieldData.ToString();
}
}
还是定义了一个取得数据的方法,和前面不同的是这个方法将返回一个IEnumerable<SamLookupData>类型。
Code
public static IEnumerable<SamLookupData> GetLookupEnum(string tableName, string lookupField, string resultField, string whereCondition)
{
IEnumerable<SamLookupData> ret = null;
string sql = string.Format("select SAM_TextField={0},SAM_ValueField={2} from {1} where {3}", resultField, tableName, lookupField,
string.IsNullOrEmpty(whereCondition) ? "1=1" : whereCondition);
ret = new MRPTablesDataContext().ExecuteQuery<SamLookupData>(sql);
return ret;
}
看得出,我还是在拼这个SQL语句。
取得了这个之后,绑定到前端的dropdownlist就简单了。
在这个代码中我加入一个空值的item,便于将值设置为空。
Code
protected void Page_Load(object sender, EventArgs e)
{
selectedValue = DropDownList1.SelectedValue;
string tableName = GetUIHintArg("TableName");
string lookupField = GetUIHintArg("LookupField");
string resultField = GetUIHintArg("ResultField");
string whereCondition = GetUIHintArg("WhereCondition");
List<SamLookupData> list=SamDBHelper.GetLookupEnum(tableName, lookupField, resultField, whereCondition).ToList();
list.Insert(0, new SamLookupData() { SAM_TextField = "-", SAM_ValueField = null });
DropDownList1.DataSource = list;
DropDownList1.DataTextField = "SAM_TextField";
DropDownList1.DataValueField = "SAM_ValueField";
DropDownList1.DataBind();
}
protected override void OnDataBinding(EventArgs e)
{
base.OnDataBinding(e);
if (Mode == DataBoundControlMode.Edit && !string.IsNullOrEmpty(FieldValueString))
{
try
{
DropDownList1.SelectedValue = FieldValueString;
}
catch
{
DropDownList1.SelectedIndex = 0;
}
}
else if((Mode==DataBoundControlMode.Insert)||((Mode == DataBoundControlMode.Edit && string.IsNullOrEmpty(FieldValueString))))
{
DropDownList1.SelectedIndex = 0;
}
}
为了更改生效,需要重载方法ExtractValues,代码如下:
Code
protected override void ExtractValues(IOrderedDictionary dictionary)
{
object val = null;
if (!string.IsNullOrEmpty(selectedValue))
{
val = selectedValue;
}
dictionary[Column.Name] = val;
}
到此这个问题已经得到了圆满解决。