自定义DataSourceControl上的数据序列化

本文关键字:数据 序列化 DataSourceControl 自定义 | 更新日期: 2023-09-27 17:57:56

我正在编写一个自定义DataSourceControl,它基本上具有与普通ObjectDataSource一样的Select功能,具有TypeNameSelectMethod属性。

来自TypeName类的数据将保存在由参数值的哈希和ContextName索引的文件中。这意味着,每当GridView请求DataSource并给出相同的参数值时,控件都会找到相应的File并从中加载数据。事实上,每个不同的参数值组合都会生成一个带有数据的新文件。

在某些情况下,当数据需要很长时间才能从数据库中处理和检索,并且不需要对用户实时(总是从最后一天开始)时,此功能可能非常有用。

我遇到的主要困难是序列化来自SelectMethod的数据。因为我只知道返回类型将是IEnumerable的一个实例。我使用XMLSerializer来保存和检索文件中的数据内容,但在尝试序列化时,它会给我一个错误Cannot serialize interface

这是执行SelectMethod并执行序列化部分的基本代码:

//Gets the select type
Type selectType = Type.GetType(TypeName);
//Gets the select method
MethodInfo selectMethod = selectType.GetMethod(SelectMethod, BindingFlags.Instance | BindingFlags.Public);
//Creates a new instance of the TypeName class
object selectInstance = Activator.CreateInstance(selectType);
//Executes the select method
object selectResult = selectMethod.Invoke(selectInstance, parameters);
IEnumerable list = (IEnumerable)selectResult;
//Create a serializer
XmlSerializer serializer = new XmlSerializer(typeof(IEnumerable));
//Writes to the XML file
using (XmlWriter writer = XmlWriter.Create(filePath))
{
    serializer.Serialize(writer, list);
}

我会使用此代码来反序列化:

//Creates the XML File
XmlSerializer deserializer = new XmlSerializer(typeof(IEnumerable));
IEnumerable list = null;
//Reads from the XML file
using (XmlReader writer = XmlReader.Create(filePath))
{
    list = (IEnumerable) deserializer.Deserialize(writer);
}

如何将Select Method结果序列化/反序列化为XML?

更新:

我尝试使用System.Web.UI.LosFormatter对数据进行序列化/反序列化。它使用IEnumerable实例执行这两个操作,但我必须在实体上放置Serializable属性。然而,在从文件中检索数据时,我注意到与XMLSerializer相比,性能有显著差异。System.Web.UI.LosFormatter在我的特定测试(4MB文件)上的反序列化速度慢了4倍。与XMLSerializer tho相比,数据文件的大小将是其大小的一半。所以,对我来说,XMLSerializer仍然是最好的选择。

更新2:

尝试使用ServiceStack JsonSerializer进行一个简单的测试,代码如下:

List<Dummy> dummies = new List<Dummy>();
dummies.Add(new Dummy() { Name = "name" });
dummies.Add(new Dummy() { Name = "name1" });
dummies.Add(new Dummy() { Name = "name2" });
IEnumerable enumerableThing = dummies;
string filePath = Path.Combine(Server.MapPath("~/App_Data"), "data2.json");
using (StreamWriter writer = new StreamWriter(filePath))
{
    JsonSerializer.SerializeToWriter<IEnumerable>(enumerableThing, writer);
}
using (StreamReader reader = new StreamReader(filePath))
{
    enumerableThing = JsonSerializer.DeserializeFromReader<IEnumerable>(reader);
}

它确实可以串行化,但当尝试DeserializeFromReader时,它会给我错误"Type System.Collections.IEnumerable is not of type IDictionary<,>"。有什么办法让我做这件事吗?

自定义DataSourceControl上的数据序列化

好吧,正如您已经发现的,您不能序列化接口。。。也就是说,你可能能够解决这个问题,但仍然对实际的对象类型"一无所知":

首先,任何IEnumerable都可以通过Cast/ToArray组合转换为数组

var enumerableThing = Foo.GetEnumerable();    
var asArray = enumerableThing.Cast<object>().ToArray();

第二,"已知类型"的数组是可串行化的

var allContainedTypes = asArray.Select(x => x.GetType()).Distinct().ToArray();
var ser = new XmlSerializer(asArray.GetType(), allContainedTypes);

var ser=新的XmlSerializer(asArray.GetType())

var sb = new StringBuilder();
using(var sw = new StringWriter(sb))
using(var xw = XmlWriter.Create(sw))
    ser.Serialize(xw, asArray);
sb.ToString().Dump();

或者,加在一起:

void Main()
{
    var enumerableThing = Foo.GetEnumerable();    
    var asArray = enumerableThing.Cast<object>().ToArray();
    var allContainedTypes = asArray.Select(x => x.GetType()).Distinct().ToArray();
    var ser = new XmlSerializer(asArray.GetType(), allContainedTypes);
    var sb = new StringBuilder();
    using(var sw = new StringWriter(sb))
    using(var xw = XmlWriter.Create(sw))
        ser.Serialize(xw, asArray);
    sb.ToString().Dump();
}

public class Foo
{
    public static IEnumerable GetEnumerable()
    {
        return new[] { 1, 2, 3, 4, 5, 6, 7 };
    }
    public static IEnumerable GetEnumerable2()
    {
        return new object[] { "1", 2, "bob", 4, null, 6, 7 };
    }
}

哦,这产生了格式:

<?xml version="1.0" encoding="utf-16"?>
<ArrayOfAnyType 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <anyType xsi:type="xsd:int">1</anyType>
    <anyType xsi:type="xsd:int">2</anyType>
    <anyType xsi:type="xsd:int">3</anyType>
    <anyType xsi:type="xsd:int">4</anyType>
    <anyType xsi:type="xsd:int">5</anyType>
    <anyType xsi:type="xsd:int">6</anyType>
    <anyType xsi:type="xsd:int">7</anyType>
</ArrayOfAnyType>

编辑:反序列化确实会带来一些问题——然而,你可以做一些类似于的事情

// Least common denominator...
object[] tempResult;
var assemblyStuffIsFrom = Assembly.GetExecutingAssembly();
var allSerializableTypes = assemblyStuffIsFrom
    .GetTypes()
    .Where(t => t.GetCustomAttributes(typeof(SerializableAttribute), true).Any())
    // TODO: add as much filtering as you can here to help trim down the set
    .ToArray();
var hope = new XmlSerializer(typeof(object[]), allSerializableTypes);
using(var sr = new StringReader(sb.ToString()))
using(var xr = XmlReader.Create(sr))
    tempResult = ((object[])hope.Deserialize(xr));