Я боюсь, что это будет большая установка для простого вопроса. Что касается сложности ответа, я боюсь, во что я могу ввязываться ...
Я создаю приложение, которое будет использоваться для преобразования данных из исходной базы данных с одной структурой таблицы в целевую базу данных с другой структурой. Целевая база данных уже будет содержать данные, и, следовательно, процесс должен иметь возможность поддерживать отношения на основе идентификаторов от источника при вставке в цель, где вновь вставленные элементы получат новые идентификаторы. Предположим, что каждую исходную таблицу можно преобразовать в одну целевую таблицу.
Минимальный код с необходимой структурой класса / интерфейса:
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>;
}
Простой вопрос! Но я не знаю, как мне сделать эту последнюю часть :(
3 ответа
Вам необходимо проверить:
- Если
Type
для текущего элементаentityList
представляет общий тип - Если этот универсальный тип представляет
EntityList<>
- Если общий аргумент этого типа имеет переданный тип
Попробуй это:
if (entityList.GetType().IsGenericType &&
entityList.GetType().GetGenericTypeDefinition() == typeof(EntityList<>) &&
entityList.GetType().GetGenericArguments()[0] == type)
{
...
}
Изменить. Получал общие аргументы неправильного типа. Фиксированный.
Хорошо, удалось сделать эту работу, используя немного 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
тривиально.
Используйте Linq:
Dictionary<string, Type> a = new Dictionary<string, Type>();
var allOfMyType = a.Where(x=> (x.Value.Name == "MyType"));
Похожие вопросы
Новые вопросы
c#
C# (произносится как «see Sharp») — это высокоуровневый мультипарадигменный язык программирования со статической типизацией, разработанный Microsoft. Код C# обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, которое включает в себя .NET, .NET Framework, .NET MAUI и Xamarin среди прочих. Используйте этот тег для ответов на вопросы о коде, написанном на C#, или о формальной спецификации C#.
true
, а третий - нет. Похоже, чтоentityList.GetType().GetGenericTypeDefinition().GetGenericArguments()[0]
возвращаетT
из исходного объявленияEntityList<T>
, а не конкретный тип, с которым был созданEntityList
. Есть ли способ попытаться преобразовать переменнуюentityList
вEntityList<passed-in type>
, что вызовет исключение, если это не подходит?