Общие обновления DataTable и TableAdaptor с помощью Reflection (C #) - PullRequest
2 голосов
/ 05 августа 2009

В моем приложении есть несколько строго типизированных наборов данных. Написание методов обновления данных становится утомительным, так как у каждого есть несколько таблиц. Я хочу создать одну универсальную функцию, чтобы я мог легко обновить все таблицы. Я не против, если мне придется создать один из них для каждого 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;

Я адаптировал это из другого фрагмента кода другого разработчика в другом приложении. Основное отличие к настоящему времени состоит в том, что он передает массив (объекта типа) dataadaptors и каждый из трех наборов данных (глобально создаваемый) устанавливается как отдельный блок if внутри блока foreach (ParameterInfo oPI в oParamaterInfoArray) (где my ' NameOfDataSet 'будет одним из наборов данных)

Кто-нибудь может дать мне небольшой толчок (или толчок?) В направлении завершения этой функции? Я знаю, что я тут, но такое ощущение, что я что-то перебрал. Этот код компилируется без ошибок.

Ответы [ 3 ]

2 голосов
/ 04 декабря 2009

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

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

Разве вы не можете относиться к ним как к их базовым классам, DbDataAdapter, DataSet и DataTable?

Вы можете получить доступ к таблице по имени, выполнив DataSet.Tables ["name"]. Это возвращает объект DataTable, который вы можете передать методу обновления DbDataAdapters.

Или, если ваш TableAdapter обновляет все таблицы в вашем DataSet, тогда вы можете передать весь DataSet напрямую в метод обновления.

С учетом сказанного я бы предложил вам пересмотреть использование типизированных наборов данных, если у вас есть такая возможность. По моему опыту, они стали хлопотами в обслуживании и использовании и обнаружили, что общие классы DataTable, DataSet и DbDataAdapter гораздо проще использовать напрямую.

0 голосов
/ 07 августа 2009

Вы действительно хотите, чтобы отражение использовалось так много в вашем DAL? Возможно, ORM, такой как LINQ to SQL или NHibernate, будет хорошей альтернативой?

...