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

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

Минимальный код с необходимой структурой класса / интерфейса:

public interface IDataSetStorable { }

public class InMemoryDataSet : List<IDataSetStorable>
{
    public AbstractDataEntity FindEntity(string id, Type type)
    {
        // The question will be about this method
        return new object() as AbstractDataEntity;
    }
}

public class EntityList<T> : Dictionary<string, T>, IDataSetStorable where T : AbstractDataEntity
{
    public void AddEntity(T entity)
    {
        this.Add(entity.ID, entity);
    }
}

public abstract class AbstractDataEntity
{
    public string ID { get; set; }
}

public abstract class DataItem<S, T> : AbstractDataEntity { }


// There will be a set of these three classes per source DB table
public class SourceType { }
public class TargetType { }
public class TransformationType : DataItem<SourceType, TargetType> { }

InMemoryDataSet содержит таблицы, представленные экземплярами (например) EntityList<TransformationType>. Будет TransformationType для каждого отображения SourceType в TargetType, где каждый из них, вероятно, будет классом из DataContext. Для каждой исходной таблицы БД будет по одной, хотя многие из этих таблиц могут быть сопоставлены с одной целевой таблицей БД.

Использование IDataSetStorable в качестве интерфейса маркера позволяет хранить EntityList<> с множеством различных подтипов в экземпляре InMemoryDataSet.

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

Экземпляр InMemoryDataSet предоставит средство поиска, которому следует передать идентификатор (из исходной БД) и параметр типа Type, представляющий TransformationType, который имеет дело с преобразованием типа просматриваемого объекта.

Пример этого: Table1 имеет два поля, id и table2_id, последнее ссылается на Table2 и его поле id. Вызов поиска будет (вроде псевдокода-y):

var entity = myDataSet.FindEntity([table1.table2_id], typeof(Table2TransformationType));

Тогда entity должен иметь тип Table2TransformationType (унаследованный в конечном итоге от AbstractDataEntity) и представлять строку из Table2 с идентификатором, соответствующим переданному методу.

И напоследок к вопросу:

В методе FindEntity(), как я могу узнать, присутствует ли EntityList<whatever the passed type was>? Моя мысль заключалась в том, чтобы использовать что-то вроде:

foreach (var entityList in this)
{
    // work out if entityList is an EntityList<passed-in type>;
}

Простой вопрос! Но я не знаю, как мне сделать эту последнюю часть :(

1
Matt Sach 13 Окт 2010 в 22:11

3 ответа

Лучший ответ

Вам необходимо проверить:

  1. Если Type для текущего элемента entityList представляет общий тип
  2. Если этот универсальный тип представляет EntityList<>
  3. Если общий аргумент этого типа имеет переданный тип

Попробуй это:

if (entityList.GetType().IsGenericType && 
    entityList.GetType().GetGenericTypeDefinition() == typeof(EntityList<>) && 
    entityList.GetType().GetGenericArguments()[0] == type) 
{
    ...
}

Изменить. Получал общие аргументы неправильного типа. Фиксированный.

1
Kirk Woll 14 Окт 2010 в 17:47
Определенно добираюсь туда. Первые два оператора дают результат true, а третий - нет. Похоже, что entityList.GetType().GetGenericTypeDefinition().GetGenericArguments()[0] возвращает T из исходного объявления EntityList<T>, а не конкретный тип, с которым был создан EntityList. Есть ли способ попытаться преобразовать переменную entityList в EntityList<passed-in type>, что вызовет исключение, если это не подходит?
 – 
Matt Sach
14 Окт 2010 в 14:05
@ Мэтт, приношу свои извинения, я должен был сначала протестировать. Я обновил ответ, но получил общие аргументы неправильного типа. Должно работать сейчас.
 – 
Kirk Woll
14 Окт 2010 в 17:48
Это прибито, +1 и отмечено как принято. Мой собственный ответ работает, но ваш был раньше и намного лучше, чем полагаться на исключение приведения из Invoke!
 – 
Matt Sach
15 Окт 2010 в 19:47

Хорошо, удалось сделать эту работу, используя немного Reflection. Кирк Уолл заставил меня искать в нужных местах, хотя в итоге решение не использовало его предложения. В классе EntityList<T> есть дополнительный метод, public T RetrieveEntity(string id), чтобы упростить извлечение отдельного элемента из Dictionary по ключу при использовании Type.GetMethod():

public class EntityList<T> : Dictionary<string, T>, IDataSetStorable where T : AbstractDataEntity
{
    public void AddEntity(T entity)
    {
        this.Add(entity.ID, entity);
    }
    public T RetrieveEntity(string id)
    {
        return this[id];
    }
}

Тогда у нас есть внутренности метода FindEntity(string id, Type type):

public class InMemoryDataSet : List<IDataSetStorable>
{
    public AbstractDataEntity FindEntity(string id, Type type)
    {
        // Make an instance of the passed-in type so that invoking
        // TryGetValue will throw an exception if operating on an
        // EntityList which is not of the correct type.
        var sample = type.GetConstructor(new Type[]{}).Invoke(new object[]{});
        foreach (var entityList in this)
        {
            try
            {
                // This doesn't manage to set sample to the found entity...
                bool idFound = (bool)entityList.GetType().GetMethod("TryGetValue").Invoke(entityList, new object[] { id, sample });
                if (idFound)
                {
                    // So we dig it out here with the method added to EntityList<>
                    sample = entityList.GetType().GetMethod("RetrieveEntity").Invoke(entityList, new object[] { id });
                    return (AbstractDataEntity)sample;
                }
            }
            catch (Exception ex)
            {
                // Likely some kind of casting exception
            }
        }
        return null;
    }
}

В момент вызова FindEntity() мы знали, что это за желаемый тип, поэтому приведение возвращаемого AbstractDataEntity тривиально.

1
Matt Sach 14 Окт 2010 в 16:09

Используйте Linq:

           Dictionary<string, Type> a = new Dictionary<string, Type>();
        var allOfMyType = a.Where(x=> (x.Value.Name == "MyType"));
0
Aliostad 13 Окт 2010 в 22:14