2015-02-10 19 views
0

我正在寻找一种简单的方式将任何数据对象存储在SQL Server中,而无需先定义表。将某种类型的IEnumerable存储到新的SQL Server数据库表(代码优先)的简单方法C#

想想这个伪代码anomyous型(LINQ)的创建的IEnumerable:

var result = from item in items select new { item.First, item.Last, Age = 42 }; 

我正在寻找一个简单的解决方案,一个函数调用是这样的:

// StoreResultInNewTable(database/context, tablename, result); 

我首先意识到EF6和代码,但我不想定义显式类型(类)。而且我不需要像缓存数据或详细跟踪数据库布局这样的实体框架的其他部分。如果表已经存在并且对象不能存储在那里,则引发错误。否则(创建表)并插入数据。

数据插入不应该太慢(SqlBulkCopy/BulkInsert)。

编辑:我真的寻找一种解决方案,其中结果集存储为SQL服务器中的普通数据库表,这意味着将.NET类型字符串的属性存储为(n)varchar,小数金钱等等。数据库中的列名应该是1:1属性名称。我对细节很灵活,但应该类似于EF6映射(没有明确定义的类型)。 没有键值存储,没有存储序列化对象,没有NoSQL,没有平面文件。

编辑2:为了使这更清晰,我给关于我的例子类型的详细信息:

class Person 
{ 
    public string First {get; set;} 
    public string Last {get; set;} 
} 

IEnumerable<Person> items = ... 

这意味着result一些IEnumerable<TypeWithoutName>。编译器是唯一已知名称TypeWithoutName,但我可以以类型安全的方式使用它,例如,通过LINQ。我很确定这种类型可以通过反思进行检查。

正如在评论中提到的:我正在寻找一个ORM,它采用result中的匿名类型,并构建一些带两个nvarchar列和一个整数列(以及相应的插入)的create table语句。

+0

为什么不把它保存到一个平面文件 – rajeemcariazo 2015-02-10 17:42:08

+1

这听起来像你希望使用一个RDBMs正是它*不是*设计的。研究像MongoDB这样的NoSQL解决方案来存储这样的数据。 另一个潜在的选择,如果你必须使用SQL Server ...我想......会创建一个xml/json对象的表示并将它们存储在一个列中。然而,这将会使查询颇为困难。 – Kritner 2015-02-10 17:42:52

+1

SQL Server(作为关系数据库)无法做到这一点,它需要具有预定义模式的表。您可能希望改为查找文档数据库选项。 – haim770 2015-02-10 17:43:24

回答

1

这听起来像你希望使用RDBMs正是它所设计的不是。研究像MongoDB这样的NoSQL解决方案来存储这样的数据。

另一个潜在的选择,如果你必须使用SQL Server ...我想...将创建一个XML/JSON表示形式的对象,并将它们存储在一个表中。然而,这将会使查询颇为困难。

的表如上面描述的可以被认为是键/值对存储,类似于以下:

CREATE TABLE keyValuePairs (
    key varchar(200) not null primary key , 
    value xml 
) 

CREATE TABLE keyValuePairs (
    key varchar(200) not null primary key , 
    value varchar(max) 
) 

在第一你可以存储你的对象作为xml,第二个是json(或者技术上的xml)。你需要根据适当的键来查询你的表,或者做一些非常奇特的查询工作 - 假设你的值的“模式”可以根据存储的对象类型而不同。

+0

我不是在寻找一个关键的价值商店或NoSql数据库。我真的想在SQL服务器中使用一个简单的表,以便具有SQL知识的人可以检查数据。 我需要类似ORM的匿名类型(反射?)。将添加详细信息到我的问题。 – stb 2015-02-10 18:51:42

1

,因为我无法找到一个现有的解决我的问题,我砍死了一些代码:

internal static void StoreEntitiesToDatabase<T>(this IEnumerable<T> elements, SqlConnection connection, 
    string tablename) 
{ 
    var sbc = new SqlBulkCopy(connection); 
    { 
     var table = new DataTable(); 
     Type listType = typeof (T); 
     foreach (PropertyInfo propertyInfo in listType.GetProperties()) 
     { 
      table.Columns.Add(propertyInfo.Name, propertyInfo.PropertyType); 
      sbc.ColumnMappings.Add(propertyInfo.Name, propertyInfo.Name); 
     } 
     foreach (T value in elements) 
     { 
      DataRow dr = table.NewRow(); 
      foreach (PropertyInfo propertyInfo in listType.GetProperties()) 
      { 
       dr[propertyInfo.Name] = propertyInfo.GetValue(value, null); 
      } 
      table.Rows.Add(dr); 
     } 

     string sqlsc = "CREATE TABLE " + tablename + "("; 
     for (int i = 0; i < table.Columns.Count; i++) 
     { 
      sqlsc += "[" + table.Columns[i].ColumnName + "] "; 
      int maxlen = table.Columns[i].MaxLength; 
      if (maxlen == -1) maxlen = 255; 
      if (table.Columns[i].DataType.ToString().Contains("System.Int32")) 
       sqlsc += " int "; 
      else if (table.Columns[i].DataType.ToString().Contains("System.Int64")) 
       sqlsc += " bigint "; 
      else if (table.Columns[i].DataType.ToString().Contains("System.DateTime")) 
       sqlsc += " datetime "; 
      else if (table.Columns[i].DataType.ToString().Contains("System.String")) 
       sqlsc += " nvarchar(" + maxlen + ") "; 
      else if (table.Columns[i].DataType.ToString().Contains("System.Double")) 
       sqlsc += " float "; 
      else if (table.Columns[i].DataType.ToString().Contains("System.Decimal")) 
       sqlsc += " money "; 
      else 
       throw new Exception("no mapping for " + table.Columns[i].DataType); 

      if (table.Columns[i].AutoIncrement) 
       sqlsc += " IDENTITY(" + table.Columns[i].AutoIncrementSeed.ToString() + "," + 
          table.Columns[i].AutoIncrementStep.ToString() + ") "; 
      if (!table.Columns[i].AllowDBNull) 
       sqlsc += " NOT NULL "; 
      sqlsc += ","; 
     } 
     sqlsc = sqlsc.Substring(0, sqlsc.Length - 1) + ")"; 
     SqlCommand cmd = new SqlCommand(sqlsc, connection); 
     cmd.ExecuteNonQuery(); 
     sbc.DestinationTableName = tablename; 
     sbc.WriteToServer(table); 
    } 
} 

可以这样调用:

using (var conn = new SqlConnection(connectionString)) 
{ 
    conn.Open(); 
    elems.StoreEntitiesToDatabase(conn, "myTable"); 
    conn.Close(); 
} 

以上代码工作得很好为我的问题,并支持匿名类型的元素。

Sidenode:我第一次尝试使用这种“欺骗” EF6:

internal class DbQuickInsert<T> : DbContext where T : class 
{ 
    public DbSet<T> MyRecords { get; set; } 

    public DbQuickInsert(string databasename) : base(databasename) 
    { 
    } 
} 

internal static class HelperQuick 
{ 
    public static void InsertIntoDatabase<T>(this IEnumerable<T> records, string databasename) where T : class 
    { 
     var qi = new DbQuickInsert<T>(databasename); 
     qi.Configuration.AutoDetectChangesEnabled = false; 
     qi.BulkInsert(records); 
     qi.SaveChanges(); 
    } 
} 

后者的代码编译,但由于实体Frameworkcannot处理匿名类型引发运行时错误。

+0

似乎有趣的事情开始,但需要更多的工作和问题的答案。现在,如果看起来只能这样插入一次,如果表存在,会发生什么?如果您为竞争的IEnumerable模式传递相同的“表名”会发生什么?我认为EF的出现是有原因的 - 这是一个有趣的想法,但已经有了解决方案,而不是(必然)在SQL服务器中。 – Kritner 2015-02-11 12:58:44

+0

是的,我的解决方案只是一个快速入门。它不仅缺乏对现有表格的处理,而且还缺少对其他数据类型的处理。可能会添加代码以确保兼容性(检查'table'是否具有现有表的列子集或是否可以扩展表等)并提高代码质量(字符串处理过多=>运行时容易出错)。 – stb 2015-02-11 13:52:08

相关问题