先闲话一番,因为最近接到的一个项目,有一个要求是在完全断网的情况下不影响软件使用,且交易数据不会丢失。需要基于云平台做开发,且别人的平台只支持.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组件对本地文件的读取(http://www.cnblogs.com/aijie/archive/2011/09/22/2185453.html),这里就只附上存入的代码,如下:
点击写入本地按钮,后置代码中自动生成按钮的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好用可无法存储到指定目录。