通用DataTable和;使用Reflection(C#)更新TableAdapter

时间:2009-08-05 18:06:02

标签: c# reflection datatable generics tableadapter

我的应用程序中有几个强类型数据集。编写更新数据的方法变得繁琐,因为每个表都有几个表。我想创建一个通用函数,我可以轻松更新所有表。我不介意我是否必须为每个DataSet创建其中一个,但如果一个函数可以处理所有这些,那将是惊人的!

将有任意数量的新记录,更新记录或已删除记录,并且每行应正确标记。此功能应该只是处理实际的保存。以下是我到目前为止的情况:

    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;

我已经从另一个开发人员在另一个应用程序中的另一段代码中调整了这个。到目前为止,主要的区别是他传入一个数据适配器的数组(类型对象),并且如果在foreach(oParamaterInfoArray中的ParameterInfo oPI)块中的块(我的',那么)将三个DataSet(全局实例化)中的每一个设置为单独的。 NameOfDataSet'将是其中一个数据集)

任何人都可以在完成此功能的过程中给我一点推动(或推动?)?我知道我就在那里,但感觉就像是在寻找一些东西。这段代码编译时没有错误。

3 个答案:

答案 0 :(得分: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;
    }

答案 1 :(得分:1)

您不能将它们视为基类,DbDataAdapter,DataSet和DataTable吗?

您可以通过执行DataSet.Tables [“name”]按名称访问表。这将返回一个DataTable对象,您可以将该对象传递给DbDataAdapters更新方法。

或者,如果TableAdapter更新DataSet中的所有表,则可以直接将整个DataSet传递给update方法。

如果你有机会这样做,我会建议你重新考虑使用类型化数据集。根据我的经验,他们最终难以维护和使用,并且发现一般的DataTable,DataSet和DbDataAdapter类更容易直接使用。

答案 2 :(得分:0)

你真的希望在DAL中使用反射吗?也许像LINQ to SQL或NHibernate这样的ORM会是一个很好的选择吗?