先闲话一番,因为最近接到的一个项目,有一个要求是在完全断网的情况下不影响软件使用,且交易数据不会丢失。需要基于云平台做开发,且别人的平台只支持.net Framework4.0((~ o ~)~zZ)。最终决定采用SilverLight来做这个项目,又是无尽的学啊……
起因:交易数据(都是实体)需要以XML的形式存储在本地,断网时能够正常操作软件。
难点:1:Silverlight 4 API仅支持存取“我的文档”,“我的音乐”,“我的图片”和“我的视频”目录以及“Program Files”和“Cookies”目录
2:将实体对象换成XML文件
思考:是否可以通过非SL类来实现本地操作;实体转XML通过反射实现
带着问题开始无限的coding了……
先建立实体类(Order对象)
public class Order { private int id; [ColumnAttribute("UserID")] public int UserID { get { return id; } set { id = value; } }
private string userName; [ColumnAttribute("UserName")] public string UserName { get { return userName; } set { userName = value; } }
string age; public string UserAge { get { return age; } set { age = value; } }
}
还需要实现一个特性(这个是为了操作只需要的数据准备的,当然这个项目中用到特性的地方只对要写入XML的实体进行筛选,因为不是所有实体属性都要写入xml文件)
[AttributeUsage(AttributeTargets.Property)] public class ColumnAttribute : Attribute { public ColumnAttribute(string property) { this.Property = property; }
public string Property { get; set; } }
然后开始写实体生成XML文件的方法了(不想新建类,我就在Order下新建了一个Update方法)
public string Update(User user) { StringBuilder sb = new StringBuilder(); sb.AppendLine(@"<?xml version=""1.0"" encoding=""utf-8""?>"); sb.AppendLine(@"<ArrayOfOrder>"); sb.AppendLine(@" <Order>"); PropertyInfo[] pro_base = user.GetType().GetProperties();
for (int i = 0; i < pro_base.Length; i++) { object[] obj = pro_base[i].GetCustomAttributes(typeof(ColumnAttribute), false);//获取用户自定义的特性 if (null != obj && obj.Length != 0) //这里用特性进行筛选哪些实体属性不需要 { string startElement = string.Format(" <{0}>", (obj.GetValue(0) as ColumnAttribute).Property);//XML元素的起始标签 string stringElement = pro_base[i].GetValue(user, null).ToString().Trim();//XML元素的标签值
string endElement = string.Format(@"</{0}>", pro_base[i].Name);//XML元素的结束标签 sb.AppendLine(startElement + stringElement + endElement); }
} sb.AppendLine(@" </Order>"); sb.AppendLine(@"</ArrayOfOrder>"); return sb.ToString();
}
然后我在MainPage.xaml中添加了一个按钮(转换)和按钮(写入本地)和一个文本框(显示Order对象调用Update的返回结果)
在按钮的Click事件中加了下面的代码(仅仅是查看实体类转换成XML的方法)
private void button3_Click(object sender, RoutedEventArgs e) { Order o = new Order{ UserID = 1, UserName = "Roy" };//实例化Order对象并赋初始值 this.txtShow.Text = o.Update(o); }
最后txtShow的文本框显示的结果如下:
<?xml version="1.0" encoding="utf-8"?><ArrayOfOrder> <Order> <UserID>1</UserID> <UserName>Roy</UserName> </Order></ArrayOfOrder>
最后面我们就只要想办法保存到本地了,上篇文章说到COM组件对本地文件的读取(),这里就只附上存入的代码,如下:
点击写入本地按钮,后置代码中自动生成按钮的Click事件,在事件中添加代码
using (dynamic dym = AutomationFactory.CreateObject("Scripting.FileSystemObject")) { dynamic write = dym.CreateTextFile(@"F:/order.xml", 1, false); write.WriteLine(this.txtShow.Text); write.Close(); }
本来我想用最简单的方法来实现上述实体转换xml的功能,那就是
PropertyInfo[] pro_base = order.GetType().GetProperties();
for (int i = 0; i < pro_base.Length; i++) { switch(pro_base[i].Name)
case “UserID”:
string id = string.Format(@"<{0}>{1}</{0}>",pro_base[i].Name,order.UserID);
break;
……
}
上面的写法不好在于要上传的每个实体类都要这样写,而且新增表或者新增列,上面的代码也需要更改。所以采用反射可以对方法进行封装,而且新增列只需要在实体类中新增一个属性就可以了。当然用于正式项目中上述代码还需要优化。Silverlight中通过COM组件没有直接存储XML文件的方法(我比较笨,没找到),就采用上述方法来做了。在COM组件上没找到直接可以写XMLWriter和XDocument写出的值的方法,有高招还希望不吝赐教。XMLWriter和XDocument好用可无法存储到指定目录。