Вам нужно высмеивать все, включая слой вашей базы данных. Если вы обращаетесь к своей базе данных через экземпляр IRepository
, то вы должны создать что-то вроде этого:
public interface IRepository
{
List<Student> GetAllStudents();
void AddStudent(Student student);
Teacher GetStudentTeacher(int studentId);
}
public class MockRepository : IRepository
{
public static List<Student> Students { get; set; }
public static List<Teachers> Teachers { get; set; }
static MockRepository()
{
Students = new List<Student>();
}
public List<Student> GetAllStudents()
{
return Students.ToList();
}
public void AddStudent(Student student)
{
Students.Add(student);
}
public Teacher GetStudentTeacher(int studentId)
{
var student = Students.FirstOrDefault(s => s.Id == studentId);
if (student != null)
{
return Teachers.FirstOrDefault(t => t.Id == student.TeacherId);
}
return null;
}
}
Верхний уровень может быть ответственным за создание исключения в случае, если у ученика уже есть учитель, и есть попытка добавить второй.
Классический подход дизайна был бы (звезда обозначает столбец первичного ключа):
Product
ProductId*
CategoryId: FK to Category.CategroyId
Name
Category
CategoryId*
Name
Property
PropertyId*
Name
Type
CategoryProperty
CategoryId*: FK to Category.CategoryId
PropertyId*: FK to Property.PropertyId
ProductProperty
ProductId*: FK to Product.ProductId
PropertyId*: FK to Property.PropertyId
ValueAsString
Если бы можно жить с тем, что каждое значение свойства перешло бы к DB, поскольку информация о преобразовании строк и преобразовании типов хранится в таблице Property, это расположение было бы достаточно.
Запрос прошел бы примерно так:
SELECT
Product.ProductId,
Product.Name AS ProductName,
Category.CategoryId,
Category.Name AS CategoryName,
Property.PropertyId,
Property.Name AS PropertyName,
Property.Type AS PropertyType,
ProductProperty.ValueAsString
FROM
Product
INNER JOIN Category ON Category.CategoryId = Product.CategoryId
INENR JOIN CategoryProperty ON CategoryProperty.CategoryId = Category.CategoryId
INNER JOIN Property ON Property.PropertyId = CategoryProperty.PropertyId
INNER JOIN ProductProperty ON ProductProperty.PropertyId = Property.PropertyId
AND ProductProperty.ProductId = Product.ProductId
WHERE
Product.ProductId = 1
Чем больше, ГДЕ условия Вы предоставляете (conjunctively, например, использование И), тем быстрее запрос будет. Если Вы правильно индексировали свои таблицы, который является.
Как это, решение не идеально для ситуации с полнотекстовым индексированием. Дополнительная таблица, которая хранит весь текст, связанный с ProductId более денормализованным способом, могла помочь здесь. Этой таблице было бы нужно обновление через триггеры, которые прислушиваются к изменениям в таблице ProductProperty.
Если пользователь приложения должен выбрать категорию, прежде чем они смогут искать, я разделил бы Ваши продукты на различные таблицы базы данных по категориям. Это решение также обозначается тем, что у самих категорий есть так мало общее. Разрушение его по категориям также сделает каждый поиск намного быстрее, так как время не будет потрачено впустую, перерывая автомобили, когда Ваш пользователь будет искать домашнее животное.
После того как Вам разделяли продукты в к категориям, должно быть легко составить таблицы с помощью общей собственности продуктов в каждой категории. Пользовательский интерфейс Вашего приложения должен быть динамичным (я думаю о веб-форме), в этом должны измениться свойства, из которых может выбрать пользователь, когда пользователь выбирает категорию.
Обратите внимание на то, что, если у Вас будут продукты, которые Вы хотите перечисленный в нескольких категориях, то это решение приведет к дублирующимся данным в Ваших таблицах. Существует компромисс между скоростью и нормализацией при разработке базы данных. Если у Вас нет продуктов, которые помещаются в несколько категорий, то я думаю, что это будет быстрым решением (с точки зрения поисковой скорости).
Большинство людей советует для использования изменений дизайна Значения атрибута объекта (EAV). Этот дизайн является излишеством для Вашей ситуации, и это представляет целый набор проблем, например:
Если у Вас есть небольшое количество категорий, лучше использовать решение A в ответе Bogdan Maxim. Таким образом, определите продукты таблицы с атрибутами, характерными для всех категорий и одной дополнительной таблицы для каждой категории, для хранения определенных для категории атрибутов.
Только если у Вас есть бесконечное число категорий или если необходимо потенциально поддерживать другой набор атрибутов на строку в продуктах, EAV хорошее решение. Но затем Вы не используете реляционную базу данных вообще, так как EAV нарушает несколько правил нормализации.
При реальной необходимости в так большой гибкости Вы были бы более обеспеченным хранением Ваших данных в XML. На самом деле Вы могли бы изучить RDF и платформы семантической паутины как Сезам.
Вы могли бы хотеть рассмотреть тип Значения атрибута объекта расположения, где можно "отметить" каждый продукт с произвольными парами имя/значение атрибутов.
Если Вы хотите быть гибкими на своих категориях и свойствах, необходимо создать следующие таблицы:
когда Вы хотите совместно использовать категорию по mroe, чем один продукт, необходимо составить таблицу ссылки для соединения n:m:
Вы будете иметь к некоторым, участвует в Ваших запросах, но с правильными индексами, Вы shoulb смочь запросить Ваши данные быстро.
Можно попробовать это. Я не слишком уверен в фактических деталях Вашего вопроса, возможно, кто-то может помочь Вам перевести немного лучше.
5 таблиц. 3 для того, чтобы хранить данные, 2 для хранения отображений между данными.
tProduct
productID
<other product details>
tCategory
categoryID
<other category details>
tProperty
propertyID
<other property details>
tProductXCategory
productyID
categoryID
tCategoryXProperty
categoryID
propertyID
Ваши запросы должны будут присоединиться к данным с помощью таблиц отображения, но это позволит Вам иметь отличающийся многие ко многим отношениям между категорией, свойствами и продуктами.
Используйте хранимые процедуры или параметризированные запросы для вытаскивания лучшей производительности из поисков.
Вы могли попробовать что-то более объектно-ориентированное.
Products(ProductID, CategoryID, <any other common properties>)
Categories(CategoryID, Name, Description, ..)
Отсюда у Вас есть много опций, и почти все они повредят нормализацию Вашей базы данных.
Будет maintaince кошмар, если необходимо добавить новые продукты
Cars(CarID, ProductID, ..)
Pets(PetID, ProductID, ..)
SELECT <fields> FROM Cars INNER JOIN Products ON Cars.ProductID = Products.ProductID
Кошмар Maintainance для различных типов свойств (т.е. интервал, varchar, и т.д.)
CategoryProperty (CPID, Name, Type)
PropertyAssociation (CPID, PropertyID)
Properties(CategoryID, PropertyID, Name, Type)
PropertyValueInt(ProductID, CPID, PropertyID, Value)
- для интервала PropertyValueString(ProductID, CPID, PropertyID, Value)
- для строк PropertyValueMoney(ProductID, CPID, PropertyID, Value)
- за деньги
При помощи этого подхода Вы не должны будете управлять всеми свойствами в отдельной таблице, но типами значения их. В основном все включенные таблицы будут справочными таблицами. Недостаток, то, что для получения каждого значения необходимо "Случиться" для каждого типа значения.
Возьмите в памяти эти статьи (здесь и здесь) при выборе любого из этих подходов. Это сообщение форума также интересно и так или иначе связано с предметом, даже при том, что это о локализации.
Вы могли также использовать ответ Tomalak и добавить строгий контроль типов, если Вы чувствуете потребность.
Я недавно должен был сделать это, и я использую NHibernate, где у меня есть три объекта
Опция категории продуктов OptionCategory
Продукт имеет 1* Категории
Продукт имеет 1* Опция
Опция имеет 1 OptionCategory
после того как это настраивается, можно использовать кэширование Nhibernate
Удачи