Есть ли способ программным образом заполнить все таблицы в строго типизированном наборе данных? - PullRequest
3 голосов
/ 29 апреля 2010

У меня есть база данных SQL Server, для которой я создал строго типизированный DataSet (используя DataSet Designer в Visual Studio 2008), поэтому все адаптеры и команды выбора и тому подобное были созданы для меня мастером.

Это небольшая база данных со в основном статическими данными, поэтому я хотел бы при запуске загружать содержимое этой БД в свое приложение, а затем собирать отдельные фрагменты данных по мере необходимости с помощью LINQ. Вместо того, чтобы жестко кодировать каждый вызов Fill адаптера, я хотел бы посмотреть, есть ли способ автоматизировать это (возможно, с помощью Reflection).

Итак, вместо:

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 напрямую), но моя компания хочет пойти по этому пути, поэтому я исследую его. Я думаю, что идея заключается в том, что по мере добавления таблиц мы можем просто обновить DataSet, используя Designer в Visual Studio, и не нужно вносить слишком много изменений в базовый код БД.

Любая помощь была бы очень ценной. Заранее спасибо!

Ответы [ 5 ]

2 голосов
/ 22 сентября 2011

Я видел все вышеперечисленные решения, и все они хороши, они вдохновили меня на поиск моего решения, Я сделал более сжатый, я знаю, что это старый пост, но я надеюсь, что он поможет людям в будущем,

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 голос
/ 19 августа 2011

Спасибо, Майк, за очень продуманное решение. Как и вы, я искал способ сделать то, что вы сделали, и использовать тот же механизм, чтобы избежать уродливого оператора switch (в C #), который должен обрабатывать сгенерированные адаптеры таблиц для выполнения обновлений привязки данных.

Я преобразовал ваш VB-код в C #, как указано ниже. Я сделал два изменения (я использую VS Express 2010 и .NET 4.0):

  1. Я изменил метод StartWith ("tbl") на EndsWith ("TableAdapter"), поскольку ряд сгенерированных элементов в пространстве имен TableAdapters, отличных от TableAdapters, начинаются с "tbl" (если вы хотите или должны это сделать) в любом случае следуйте этому соглашению), но только TableAdapter оканчивается на TableAdapter.

  2. Я изменил вызов метода Fill, поскольку VS сообщает мне во время сборки, что объект, на который ссылается «адаптер» (который выглядит как TableAdapter в отладчике), не имеет метода Fill, и есть нет метода расширения Fill. Поэтому я не могу выполнить Fill. Я не совсем уверен, почему это не сработало. Но в любом случае я изменил его, чтобы явно найти метод Fill, а затем вызвал этот метод. Кажется, это работает.

Steve

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]});
      }   

}
1 голос
/ 29 апреля 2010

Хорошо, я думаю, что у меня все получилось, и я просто хочу поделиться результатами, если есть вероятность, что есть люди, которые такие же безумные, как и я.

По сути, все волшебство происходит с помощью нескольких запросов LINQ и рефлексии. Для целей этого примера мы примем:

  1. Существует строго типизированный набор данных, созданный с помощью конструктора наборов данных в Visual Studio 2008, который называется dsTest . Переменная уровня модуля содержит экземпляр этого DataSet и называется (достаточно соответственно): m_DataSet .
  2. Все таблицы сами соответствуют стандартному соглашению об именах SQL Server, начиная с «tbl».
  3. В рамках этого мастера была создана серия адаптеров таблиц для каждой таблицы в пространстве имен с именем dsTestTableAdapters .
  4. Каждый адаптер назван в соответствии с таблицей (поэтому, если у нас есть «tblThingy», то будет создан адаптер с именем «tblThingyTableAdapter»).
  5. Приложение находится в пространстве имен, из-за отсутствия чего-либо лучшего MyNamespace .

Вот процедура, вызываемая при загрузке формы:

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

Я попробовал это, и это сработало как шарм. Спасибо всем за помощь, и я надеюсь, что вы найдете это полезным!

1 голос
/ 01 июля 2010

Я думаю, у вас есть только одна проблема! если этот типизированный набор данных имеет отношения между таблицами, этот код не будет загружать таблицы данных в правильном порядке!

1 голос
/ 29 апреля 2010

Не существует API, который позволял бы выполнять автоматическое заполнение всего набора типизированных данных, или такой код не создается в наборе типизированных данных, который поддерживает это. Это также трудно сделать, потому что у адаптеров таблиц нет общего базового класса, который может позволить вам сделать это.

Если вам действительно нужно это сделать, вам нужно будет поддерживать коллекцию имен типов DataTable и таблиц имен TableAdapter и выполнять итерацию по коллекции, чтобы выполнить заполнение набора данных.

Поэтому я рекомендую заполнять набор данных для каждой таблицы «жестким кодом», как указано в первых примерах кода.

EDIT

Вот одно из возможных решений.

Определите интерфейс ITableAdapter следующим образом

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

Все TableAdapter являются частичными классами, поэтому вы можете расширить их и добавить свой собственный код в частичный пользовательский класс для TableAdapter. Реализуйте ITableAdapter для каждого TableAdapter в вашем наборе типизированных данных. так что это может выглядеть так.

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

Теперь вы можете перебирать каждый тип в вашей сборке и фильтровать типы типа ITableAdapter и вызывать метод SelectAll () для каждого из них, чтобы заполнить его в вашем наборе данных. :)

EDIT2

Я только что предложил другое элегантное решение этой проблемы. Все, что вам нужно сделать, это определить интерфейс ITableAdapter для сопоставления уже реализованных методов в TableAdapters, сгенерированных конструктором набора данных.

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 :)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...