2

アプリケーション全体に、厳密に型指定されたデータセットがいくつかあります。それぞれに複数のテーブルがあるため、データを更新するメソッドを記述するのは面倒です。すべてのテーブルを簡単に更新できる汎用関数を 1 つ作成したいと考えています。DataSet ごとにこれらのいずれかを作成する必要があるかどうかは気にしませんが、1 つの関数でそれらすべてを処理できるとしたら、それは素晴らしいことです!

新しいレコード、更新されたレコード、または削除されたレコードの数に制限はなく、各行に適切にフラグを立てる必要があります。この関数は、実際の保存のみを処理する必要があります。これが私がこれまでに持っているものです:

    private bool SaveData(object oTableAdaptor, object ds)
    {
        try
        {
            Type oType = oTableAdaptor.GetType();
            MethodInfo[] oMethodInfoArray = oType.GetMethods();

            foreach (MethodInfo oMI in oMethodInfoArray)
            {
                if (oMI.Name == "Update")
                {
                    ParameterInfo[] oParamaterInfoArray = oMI.GetParameters();
                    foreach (ParameterInfo oPI in oParamaterInfoArray)
                    {
                        Type DsType = null;

                        if (oPI.ParameterType.Name == "NameOfDataSet")
                        {
                            DsType = typeof(MyDataSet);

                            // get a list of the changed tables???
                        }

                        if (((DataSet)ds).HasChanges() == true)
                        {
                            if (oPI.ParameterType == DsType)
                            {
                                object[] values = { ds };
                                try
                                {
                                    oMI.Invoke(oTableAdaptor, values);
                                }
                                catch (Exception ex)
                                {
                                    System.Diagnostics.Debug.WriteLine(oTableAdaptor.GetType().Name + Environment.NewLine + ex.Message);
                                }
                            }
                        }

                    }
                }
            }
        }
        catch (Exception Exp)
        {
            System.Diagnostics.Debug.WriteLine(Exp.Message);
            if (Exp.InnerException != null) System.Diagnostics.Debug.WriteLine(Exp.InnerException.Message);

            return false;
        }

        return true;

これは、別の開発者が別のアプリケーションで使用している別のコードから採用したものです。これまでの主な違いは、データアダプターの配列 (オブジェクト型) を渡しており、(グローバルにインスタンス化された) 3 つの DataSet のそれぞれが foreach (oParamaterInfoArray の ParameterInfo oPI) ブロック内の個別の if ブロックとして設定されていることです (ここで、私の ' NameOfDataSet' はデータセットの 1 つになります)

この機能を完成させる方向で、誰か私を少し押してくれませんか? 私は自分がそこにいることを知っていますが、何かを見過ごしているように感じます. このコードはエラーなしでコンパイルされます。

4

3 に答える 3

2

私はこれを使っています。ただし、いくつかの最適化が必要になります。これにより、データセット内の関係に応じて正しい順序でテーブルが更新されます(自己参照がない場合は、行を並べ替えることで処理できますが、簡単にするためにここでは投稿しません)。

    public static void Save(DataSet data, SqlConnection connection)
    {
        /// Dictionary for associating adapters to tables.
        Dictionary<DataTable, SqlDataAdapter> adapters = new Dictionary<DataTable, SqlDataAdapter>();

        foreach (DataTable table in data.Tables)
        {
            /// Find the table adapter using Reflection.
            Type adapterType = GetTableAdapterType(table);
            SqlDataAdapter adapter = SetupTableAdapter(adapterType, connection, validityEnd);
            adapters.Add(table, adapter);
        }

        /// Save the data.
        Save(data, adapters);
    }

    static Type GetTableAdapterType(DataTable table)
    {
        /// Find the adapter type for the table using the namespace conventions generated by dataset code generator.
        string nameSpace = table.GetType().Namespace;
        string adapterTypeName = nameSpace + "." + table.DataSet.DataSetName + "TableAdapters." + table.TableName + "TableAdapter";
        Type adapterType = Type.GetType(adapterTypeName);
        return adapterType;
    }

    static SqlDataAdapter SetupTableAdapter(Type adapterType, SqlConnection connection)
    {
        /// Set connection to TableAdapter and extract SqlDataAdapter (which is private anyway).
        object adapterObj = Activator.CreateInstance(adapterType);
        SqlDataAdapter sqlAdapter = (SqlDataAdapter)GetPropertyValue(adapterType, adapterObj, "Adapter");
        SetPropertyValue(adapterType, adapterObj, "Connection", connection);

        return sqlAdapter;
    }

    static object GetPropertyValue(Type type, object instance, string propertyName)
    {
        return type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance).GetValue(instance, null);
    }

    static void SetPropertyValue(Type type, object instance, string propertyName, object propertyValue)
    {
        type.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Instance).SetValue(instance, propertyValue, null);
    }

    static void Save(DataSet data, Dictionary<DataTable, SqlDataAdapter> adapters)
    {
        if (data == null)
            throw new ArgumentNullException("data");

        if (adapters == null)
            throw new ArgumentNullException("adapters");

        Dictionary<DataTable, bool> procesedTables = new Dictionary<DataTable, bool>();
        List<DataTable> sortedTables = new List<DataTable>();

        while (true)
        {
            DataTable rootTable = GetRootTable(data, procesedTables);
            if (rootTable == null)
                break;

            sortedTables.Add(rootTable);
        }

        /// Updating Deleted rows in Child -> Parent order.
        for (int i = sortedTables.Count - 1; i >= 0; i--)
        {
            Update(adapters, sortedTables[i], DataViewRowState.Deleted);
        }

        /// Updating Added / Modified rows in Parent -> Child order.
        for (int i = 0; i < sortedTables.Count; i++)
        {
            Update(adapters, sortedTables[i], DataViewRowState.Added | DataViewRowState.ModifiedCurrent);
        }
    }

    static void Update(Dictionary<DataTable, SqlDataAdapter> adapters, DataTable table, DataViewRowState states)
    {
        SqlDataAdapter adapter = null;

        if (adapters.ContainsKey(table))
            adapter = adapters[table];

        if (adapter != null)
        {
            DataRow[] rowsToUpdate = table.Select("", "", states);

            if (rowsToUpdate.Length > 0)
                adapter.Update(rowsToUpdate);
        }
    }

    static DataTable GetRootTable(DataSet data, Dictionary<DataTable, bool> procesedTables)
    {
        foreach (DataTable table in data.Tables)
        {
            if (!procesedTables.ContainsKey(table))
            {
                if (IsRootTable(table, procesedTables))
                {
                    procesedTables.Add(table, false);
                    return table;
                }
            }
        }

        return null;
    }

    static bool IsRootTable(DataTable table, Dictionary<DataTable, bool> procesedTables)
    {
        foreach (DataRelation relation in table.ParentRelations)
        {
            DataTable parentTable = relation.ParentTable;
            if (parentTable != table && !procesedTables.ContainsKey(parentTable))
                return false;
        }

        return true;
    }
于 2009-12-04T09:50:52.803 に答える
1

それらを基本クラス、DbDataAdapter、DataSet、および DataTable として扱うことはできませんか?

DataSet.Tables["name"] を実行すると、名前でテーブルにアクセスできます。これにより、DbDataAdapters update メソッドに渡すことができる DataTable オブジェクトが返されます。

または、TableAdapter が DataSet 内のすべてのテーブルを更新する場合は、DataSet 全体を update メソッドに直接渡すことができます。

そうは言っても、機会があれば、型付きデータセットの使用を再考することをお勧めします。私の経験では、それらは維持して使用するのが面倒であり、一般的な DataTable、DataSet、および DbDataAdapter クラスを直接使用する方がはるかに簡単であることがわかりました。

于 2009-08-07T07:36:35.780 に答える
0

DAL でリフレクションをそんなに使いたいですか? おそらく、LINQ to SQL や NHibernate などの ORM は良い代替手段でしょうか?

于 2009-08-06T21:16:24.580 に答える