C#:映射数据类型

时间:2011-06-02 21:07:09

标签: c# linq data-structures

我必须编写一个可怕的界面,将数据从我们旧应用程序中的数百个数据文件导入新数据库,这些数据文件具有硬编码的所有内容(显示的数据类似于Excel电子表格,它允许我们将数据导出到逗号分隔值。)

我可以用标题名称全部阅读。

由此,我可以为要在Sql CE数据库中使用的列生成一个名称。

目前,数据包括floatintDateTimebitcharstring

我已经想出了一个方式来做这个(在我们所有的数据上都未经测试),但是非常感谢那些知道如何更好地编码的人的任何帮助。

除非有人不理解我的要求,否则下面的代码无需通读。

public enum MyParameterType { NA, Float, Bool, Char, Date, Int, String }

class MyParameter {

public MyParameter(string name, string value) {
  if (String.IsNullOrEmpty(name) || String.IsNullOrEmpty(value)) {
    throw new NotSupportedException("NULL values are not allowed.");
  }
  Name = name.Trim();
  Value = value.Trim();
  Type = MyParameterType.NA;
  if (-1 < Value.IndexOf('.')) { // try float
    float f;
    if (float.TryParse(Value, out f)) {
      string s = f.ToString();
      if (s == Value) {
        Parameter = new SqlCeParameter(AtName, SqlDbType.Float) { Value = f };
        Type = MyParameterType.Float;
      }
    }
  }
  if (Type == MyParameterType.NA) {
    bool b;
    if (bool.TryParse(Value, out b)) {
      Parameter = new SqlCeParameter(AtName, SqlDbType.Bit) { Value = b };
      Type = MyParameterType.Bool;
    }
  }
  if (Type == MyParameterType.NA) {
    if (Value.Length == 1) {
      char c = Value[0];
      Parameter = new SqlCeParameter(AtName, SqlDbType.Char) { Value = c };
      Type = MyParameterType.Char;
    }
  }
  if (Type == MyParameterType.NA) {
    DateTime date;
    if (DateTime.TryParse(Value, out date)) {
      Parameter = new SqlCeParameter(AtName, SqlDbType.DateTime) { Value = date };
      Type = MyParameterType.Date;
    }
  }
  if (Type == MyParameterType.NA) {
    if (50 < Value.Length) {
      Value = Value.Substring(0, 49);
    }
    Parameter = new SqlCeParameter(AtName, SqlDbType.NVarChar, 50) { Value = this.Value };
    Type = MyParameterType.String;
  }
}

public string AtName { get { return "@" + Name; } }

public string Name { get; set; }

public MyParameterType Type { get; set; }

public SqlCeParameter Parameter { get; set; }

public string Value { get; set; }

}

我最担心的是我不想错误地解释其中一个输入(比如布尔值为char)。

我也在寻找一种方法来比较MyParameter的新实例(即如果它少于一种类型,请尝试其他类型)。

看到一些很酷的新表达式来产生这个奖励积分!

3 个答案:

答案 0 :(得分:2)

我认为问题不在于该代码,它似乎是一种知道的方式,因为你总是接收params作为字符串,我认为你可以更好地尝试它,但在数据提取步骤中,

也许使用标题列,您可以“解析它们”到类型..

并尝试

Convert.ChangeType(yourValue, typeof(string, double,etc))

答案 1 :(得分:2)

给出一些抽象的CsvReader

using (var reader = new CsvReader(file))
{
    TableGuess table = new TableGuess { Name = file };

    // given: IEnumerable<string> CsvReader.Header { get; }
    table.AddColumns(reader.Header);

    string[] parts;
    while (null != (parts = reader.ReadLine()))
    {
        table.AddRow(parts);
    }
}

您的ColumnGuess

class ColumnGuess
{
    public string Name { get; set; }
    public Type Type { get; set; }
    public int Samples { get; private set; }

    public void ImproveType(string value)
    {
        if (this.Samples > 10) return;
        this.Samples++;

        float f; bool b; DateTime d; int i;
        if (Single.TryParse(value, out f))
        {
            this.Type = typeof(float);
        }
        else if (Boolean.TryParse(value, out b))
        {
            this.Type = typeof(bool);
        }
        else if (DateTime.TryParse(value, out d))
        {
            this.Type = typeof(DateTime);
        }
        else if (value.Length == 1 && this.Type == null && !Char.IsDigit(value[0]))
        {
            this.Type = typeof(char);
        }
        else if (this.Type != typeof(float) && Int32.TryParse(value, out i))
        {
            this.Type = typeof(int);
        }
    }
}

TableGuess将包含猜测的列和行:

class TableGuess
{
    private List<string[]> rows = new List<string[]>();
    private List<ColumnGuess> columns;

    public string Name { get; set; }

    public void AddColumns(IEnumerable<string> columns)
    {
        this.columns = columns.Select(cc => new ColumnGuess { Name = cc })
                              .ToList();
    }

    public void AddRow(string[] parts)
    {
        for (int ii = 0; ii < parts.Length; ++ii)
        {
            if (String.IsNullOrEmpty(parts[ii])) continue;
            columns[ii].ImproveType(parts[ii]);
        }

        this.rows.Add(parts);
    }
}

您可以添加TableGuess AsDataTable()方法:

public DataTable AsDataTable()
{
    var dataTable = new dataTable(this.Name);
    foreach (var column in this.columns)
    {
        dataTable.Columns.Add(new DataColumn(
            column.Name,
            column.Type ?? typeof(string)));
    }

    foreach (var row in this.rows)
    {
        object[] values = new object[dataTable.Columns.Count];
        for (int cc = 0; cc < row.Length; ++cc)
        {
            values[cc] = Convert.ChangeType(row[cc],
                dataTable.Columns[cc].DataType);
        }

        dataTable.LoadRow(values, false);
    }

    return dataTable;
}

您可以使用SqlCeDataAdapter移动DataTable中的数据(将表本身添加到数据库之后)。

答案 2 :(得分:1)

这个伪代码怎么样 - 我认为这对你来说应该足够快。这是非常伪的 - 所以“字符串”,“字符”等只是枚举值的占位符或者你想要的任何其他东西。

For first data row in data file
  For each column in row
    TypeOfCol(column) = <best first guess>
  Next
Next

For each data row in data file
  For each column in row
    If TypeOfCol(column) = "string"
      Continue For
    If TypeOfCol(column) = "char"
      If columnValue has more than one character
        TypeOfCol(column) = "string"
        Continue For
    If TypeOfCol(column) = "bit"
      If columnValue isn't 1 or 0
        TypeOfCol(column) = "int" // Might not be an int - next If will pick up on that...
    If TypeOfCol(column) = "int"
      If columnValue isn't integer
        TypeOfCol(column) = "float"
    If TypeOfCol(column) = "float"
      If columnValue isn't a float
        TypeOfCol(column) = If(columnValue has more than one character then "string" else "char")
    If TypeOfCol(column) = "datetime"
      If columnValue isn't a date/time
        TypeOfCol(column) = "string"
  Next
Next