.NET - 有没有办法以编程方式填充强类型数据集中的所有表?

时间:2010-04-29 14:27:10

标签: vb.net visual-studio-2008 reflection ado.net strongly-typed-dataset

我有一个SQL Server数据库,我为其创建了一个强类型DataSet(使用Visual Studio 2008中的DataSet Designer),因此向导为我创建了所有适配器和选择命令以及诸如此类的东西。

这是一个包含大量静态数据的小型数据库,因此我想在启动时将此数据库的内容全部提取到我的应用程序中,然后根据需要使用LINQ获取单个数据片段。而不是硬编码每个适配器填充调用,我想看看有没有办法自动化这个(可能通过反射)。

所以,而不是:

Dim _ds As New dsTest
dsTestTableAdapters.Table1TableAdapter.Fill(_ds.Table1)
dsTestTableAdapters.Table2TableAdapter.Fill(_ds.Table2)
<etc etc etc>

我更喜欢做类似的事情:

Dim _ds As New dsTest
For Each tableName As String In _ds.Tables
    Dim adapter as Object = <routine to grab adapter associated with the table>
    adapter.Fill(tableName)
Next

甚至远程可行吗?我已经做了相当多的搜索,我不认为这是一个不常见的请求,但我必须要么提出错误的问题,要么我想做这个很奇怪。

我承认我通常更喜欢使用未绑定的控件而不使用强类型数据集(我更喜欢直接编写SQL),但我的公司想要走这条路,所以我正在研究它。我认为这个想法是,随着表的添加,我们可以使用Visual Studio中的Designer刷新DataSet,而不必进行太多底层数据库代码更改。

任何帮助都会非常感激。提前谢谢!

6 个答案:

答案 0 :(得分:2)

我看到了上述所有解决方案,他们都很好,他们激励我找到我的解决方案, 我做了一个更挤压的,我知道这是一个旧帖子,但我希望它能帮助人们在未来的时间,

Private Sub FillDataSet(ByRef ds As SvuDS)
    For Each t As DataTable In ds.Tables

        Dim adType As Type = Assembly.GetExecutingAssembly.GetType("ProjectNameSpace.MyDSTableAdapters." & t.TableName & "TableAdapter")

        'Create Adapter Instance     
        Dim adapter As Object = Activator.CreateInstance(adType)

        'Fill the Table   
        adapter.GetType().GetMethod("Fill").Invoke(adapter, New Object() {t})
    Next
End Sub

我甚至可以在某种程度上推断出名称空间,但我希望它很简单,而且对我有用

答案 1 :(得分:1)

不存在任何api允许您对整个类型化数据集执行此自动填充,或者在支持此类型的数据集数据集中不生成此类代码。这样做也很困难,因为TableAdapter没有可以让你这样做的公共基类。

如果确实需要这样做,则必须维护DataTable类型名称和TableAdapter类型名称的集合,并迭代集合以执行数据集填充。

因此,我建议您以第一个代码示例的形式,以“硬代码”方式填充每个表的数据集。

修改

这是一种可能的解决方案。

定义接口ITableAdapter,如下所示

public interface ITableAdapter<TDataTable> : where TDataTable : DataTable
{
    TDataTable SelectAll();
}

所有TableAdapter都是部分类,因此您可以扩展它们并在TableAdapter的部分自定义类中添加自定义代码。在类型化数据集中的每个TableAdapter上实现ITableAdapter。所以它可能看起来像这样。

public partial class YourTableAdapter : ITableAdapter<YourDataSet.YourDataTableDataTable>
{
    public YourDataSet.YourDataTableDataTable SelectAll()
    {
         return this.GetData();
    }
}

现在,您可以迭代程序集中的每个类型并过滤ITableAdapter类型的那些类型,并在每个类型上调用SelectAll()方法将其填充到数据集中。 :)

EDIT2

我刚刚想出了另一个优雅的解决方案来解决这个问题。您需要做的就是定义Interface ITableAdapter以映射由数据集设计器生成的TableAdapter中已实现的方法。

public interface ITableAdapter<TDataTable> : where TDataTable : DataTable
{
    void Fill(TDataTable);
}

并像这样扩展你的TableAdapter部分类。

public partial class YourTableAdapter : ITableAdapter<YourDataSet.YourDataTableDataTable>
{
    //No code required here, since Fill method is already defined in TableAdapter :)
}

答案 2 :(得分:1)

好吧,我想我已经解决了这个问题,并且只想分享那些有些像我一样疯狂的人的结果。

基本上,使用一些LINQ查询和反射会发生所有魔法。出于本示例的目的,我们假设:

  1. 在Visual Studio 2008中使用DataSet Designer创建了一个强类型的DataSet,称为 dsTest 。模块级变量包含此DataSet的一个实例,并被调用(足够恰当), m_DataSet
  2. 表本身都遵循标准的SQL Server命名约定,从“tbl”开始。
  3. 作为此向导的一部分,为名为 dsTestTableAdapters 的命名空间内的每个表创建了一系列表适配器。
  4. 每个适配器都是根据表命名的(所以如果我们有“tblThingy”,那么就会创建一个名为“tblThingyTableAdapter”的适配器。)
  5. 应用程序位于名为的名称空间中,因为缺少更好的内容, MyNamespace
  6. 这是例程,名为Form Load:

    Private Sub PopulateDataSet()
        ' Get our table adapters
        Dim adapters As List(Of Type) = (From t As Type In System.Reflection.Assembly.GetExecutingAssembly.GetTypes Where t.Namespace = "MyNameSpace.dsTestTableAdapters" And t.Name.StartsWith("tbl") Select t).ToList
    
        ' Initialize our dataset
        m_DataSet = New dsUtility
    
        ' Get our table names
        Dim tableNames as List(Of String) = (From dtbl As DataTable In m_DataSet.Tables Select dtbl.TableName).ToList
    
        ' Loop through each table name and fill the table with the corresponding adapter
        For Each iter As String In tableNames
            ' Grab the corresponding adapter name 
            Dim tableName As String = iter ' Grab a copy of the table name to avoid LINQ issues with iteration variables
            Dim adapterType As Type = (From t As Type In adapters Where t.Name.StartsWith(tableName) Select t).First
    
            ' Given the adapter type name, use Reflection to create an instance
            Dim adapter As Object = Activator.CreateInstance(adapterType)
    
            ' Use the instance to fill the appropriate table
            adapter.Fill(m_DataSet.Tables(tableName))
        Next
    End Sub
    

    我试过了,它就像一个魅力。谢谢大家,感谢您的帮助,我希望您觉得这很有用!

答案 3 :(得分:1)

我认为你只有一个问题! 如果此Typed数据集具有表之间的关系,则此代码将不会以正确的顺序加载数据表!

答案 4 :(得分:1)

谢谢,迈克,这个非常周到的解决方案。和你一样,我一直在寻找一种方法去做你已经完成的事情,并使用相同的机制来避免一个丑陋的switch语句(在C#中),它必须使生成的TableAdapters执行数据绑定更新。

我将您的VB代码转换为C#,如下所示。我做了两处更改(我正在使用VS Express 2010和.NET 4.0):

  1. 我将StartWith(“tbl”)方法更改为EndsWith(“TableAdapter”),因为TableAdapters命名空间中除了TableAdapter之外的许多生成成员都以“tbl”开头(假设您需要或需要无论如何都遵循该惯例),但只有TableAdapter以“TableAdapter”结尾。

  2. 我将调用更改为Fill方法,因为VS在构建时告诉我“适配器”引用的对象(在调试器中看起来像一个TableAdapter)没有Fill方法,并且有没有填充扩展方法。因此,我不能执行填充。我不确定为什么这不起作用。但无论如何,我将其更改为显式查找Fill方法然后调用该方法。这似乎有效。

  3. 史蒂夫

    public PopulateDataSet()    {

    // Get the TableAdapters
    List<Type> tableAdapters =  (from t in 
                  System.Reflection.Assembly.GetExecutingAssembly().GetTypes()  
                  where t.Namespace == "MyNameSpace.m_DataSetTableAdapters" 
                  && t.Name.EndsWith("TableAdapter")
                  select t).ToList();
    
    // Get the DataTable names
    List<string> tableNames = (from DataTable dtbl in m_DataSet.Tables 
                   select dtbl.TableName).ToList();
    
    // Loop thru each table and fill it using the corresponding TableAdapter
    foreach (string iter in tableNames) 
          {
          string tableName = iter;  // Stopt Linq issues with iteration vbls
          Type adapterType = (from t in tableAdapters
                             where t.Name.StartsWith(tableName)
                             select t).First();
    
          // Given the adapter type name, use Reflection to create an instance
          Object adapter = Activator.CreateInstance(adapterType);
    
          // Get a reference to the Fill method of the relevant adapter
          MethodInfo method = adapter.GetType().GetMethod("Fill");
    
          // Invoke the Fill method, passing in the relevant DataTable parameter
          method.Invoke(adapter, new Object[] {m_DataSet.Tables[tableName]});
          }   
    
    }
    

答案 5 :(得分:1)

前段时间我发现了这个线程,从那以后在我的小项目中成功地使用了这种方法。但是,最近我发现它有点有限。我对数据集中的每个表适配器的查询很少,名称如“FillByContext”、“FillById”、“FillByName”等,每个都有一组不同数据类型的不同参数。所有方法都返回结构相同但内容不同的表。所以我在方法中添加了一些小的“概括”。

Private Sub MethodsAndParams(ds As DataSet,
                             dt As DataTable,
                     taParams() As Object,
                       taMethod As String)
        
        Dim taType As Type = Assembly.GetExecutingAssembly.GetType(
        "MyProjectName." +
        ds.DataSetName +
        "TableAdapters." +
        dt.TableName +
        "TableAdapter")
        Dim ta As Object = Activator.CreateInstance(taType)
        dt = ds.Tables(dt.TableName)
        ta.GetType().GetMethod(taMethod).Invoke(
    ta, New Object() {dt}.Union(taParams).ToArray)

End Sub

现在我可以将表适配器方法名称作为字符串和适当的参数集作为对象数组传递给这个例程。