Я играл со слабо связанным уровнем доступа к данным. Я нашел процесс внедрения зависимостей весьма полезным, но столкнулся с небольшой загадкой при рассмотрении использования дженериков.
Есть ли способ получить класс, который поддерживает параметр универсального типа, чтобы он действительно был как развязанным, так и безопасным? Меня беспокоит то, что даже если вы пишете в общий интерфейс, если производный тип в будущем отличается от объектов, для которых вы изначально закодировали, вы можете столкнуться с некоторыми неприятными ошибками времени выполнения, которые компилятор может не поймать.
Проблема в том, что когда я пишу код для получения данных из базы данных и гидратации моего объекта, я сталкиваюсь с головоломкой при попытке реализовать его на следующем уровне. Если я передам класс, такой как Foo2 ниже, я могу сломать свой код, поскольку нет «неявного» преобразования. Я работал с отражением, чтобы попытаться ослабить ситуацию, но я постоянно возвращаюсь к проблеме, что при получении данных из базы данных мне нужно гидратировать конкретный тип. Затем этот тип создает проблемы с приведением и контролем типов. Кроме того, если бы я хотел абстрагировать все методы для всех многих типов в моей библиотеке сущностей, я мог бы сделать это с помощью отражения, но я все еще сталкиваюсь с проблемой, что общий тип может только обеспечивать гарантии типа для явных операторов «where T: ISomeInterface». . Наконец, эта модель не сработает, если в будущем появится больше производных типов или типов, ответвляющихся от интерфейсов для формирования новых типов. Думая, что мне нужно будет реализовать новые объекты доступа к данным для каждого отдельного типа, когда-либо созданного, потребовались бы изменения на уровне доступа к данным. Это ломает определение слабой связи в моем сознании.
Все это, кажется, заставляет меня вернуться к вопросу: можно ли использовать дженерики по-настоящему слабо связанным образом? Если да, то какие ссылки вы можете предложить?
Упрощенный пример:
public interface IEntity
{
// stuff for state and methods ...
}
public interface IRepository<T> where T : IEntity
{
void Save(out int id, T obj);
T Load(int id);
}
public interface IFoo : IEntity
{
int Id { get; set; }
//All other IEntities goodness
//Some new goodness specific to Foo
}
//Concrete Entities:
public class Foo : IFoo
{
// blah blah
}
public class Foo2 : IFoo
{
// new blah blah
}
public class FooRepository : IRepository<Foo> //OOPS, Looks like we have settled in on a concrete type!
{
public void Save(out int id, Foo obj)
{
// ADO.NET code to access Sql ...
id = 1;// this would actually be the result of the Sql insert output parameter
return;
}
public Foo Load(int id)
{
Foo foo = new Foo();
// ADO.Net code to access Sql
return foo;
}
}
Итак, как бы вы поступили с кодом, который может обрабатывать любой объект, производный от IFoo, и при этом иметь возможность возвращать полностью сформированный конкретный тип независимо от того, что программисты делают в будущем? Лучше ли отказаться от общей части и придерживаться внедрения зависимостей? Любые ссылки, рекомендации и т. д. были бы замечательными.