В настоящее время я работаю над поиском хорошей структуры проекта для трехуровневого решения, чтобы избежать ненужной работы в новом проекте.
Проект будет состоять из основного продукта, который
- модельный проект, который содержит первые модели кода EF
- проект, который имеет бизнес-логику и коммуникационную логику на сервере
- проект репозитория на клиенте
- проект silverlight с представлениями и моделями представлений (здесь хотелось бы использовать caliburn.micro)
Проблема сейчас в том, что у заказчика могут быть особые требования, которые могут привести к изменениям во всех вышеперечисленных проектах. Поэтому я подумал, что могу просто использовать базовую структуру и создать такую же структуру для клиента. Если бы изменений не было, у меня были бы просто пустые классы, которые просто расширяли бы базовый класс и ничего не добавляли.
Что приводит меня к следующим проблемам:
- Является ли проблемой в Entity Framework (сначала код) иметь базовые классы в одном проекте (который уже полностью функционален) и иметь другой проект, который может расширить классы модели новым полем?
- Является ли проблема в XAML изменением пользовательского элемента управления? Например, если у меня есть пользовательский элемент управления, состоящий из пяти текстовых полей в моем ядре, и я хочу изменить второе поле на переключатель, но ничего больше.
Я также приму изменения в структуре проекта, если есть лучший способ обработки изменений, специфичных для клиента.
Редактировать: я, вероятно, буду использовать следующий подход для решения проблемы.
Структура сущности:
С кодом сначала кажется возможным, чтобы один модельный проект расширял другой проект. Это означает, что я мог бы написать что-то вроде:
public class CoreAddress{
[Key]
public int AdrId{get; set;}
public string Street {get;set;}
}
public class CustomerAddress : CoreAddress{
public string StreetNumber {get; set;}
}
Единственное, что нужно для того, чтобы это работало, — это строка внутри DbContext:
(this as IObjectContextAdapter).ObjectContext.MetadataWorkspace.LoadFromAssembly(typeof(<entity from other assembly>).Assembly);
XAML
Чтобы получить подобное поведение в XAML, мне пришлось использовать Caliburn.Micro (в сочетании с MEF), что очень помогло.
Я бы создал элементы управления UserControl, включающие элементы ContentControl, которые динамически извлекаются с помощью MEF. Это означает, что у меня снова есть основной проект со всеми представлениями и ViewModels. Если мне нужно где-то обменять специальный элемент управления для клиента, я меняю элемент управления на ContentControl и создаю для него базовое представление и ViewModel (то же самое, что было до запроса на изменение). Эта ViewModel для ContentControl аннотируется интерфейсом экспорта и ExportMetadata для установки приоритета 1. Теперь я создаю другой проект с другим UserControl, который имеет какой-то другой элемент управления вместо основного элемента управления, и снова аннотирую его как экспорт с тем же интерфейсом, но установите мой приоритет выше, и поэтому будет загружен пользовательский элемент управления.
Короткий пример для этого:
Основной пользовательский элемент управления и модель просмотра:
<UserControl x:Class="SilverlightApplication5.TestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
<ContentControl x:Name="Item"/>
<TextBox x:Name="TextItem" Text="asdf"/>
</StackPanel>
</Grid>
</UserControl>
public class TestViewModel : Screen
{
private object viewModel;
private Lazy<IMyViewModel, IPluginMetadata>[] _orderEditorFactory;
[ImportMany(typeof(IMyViewModel), AllowRecomposition = true)]
public Lazy<IMyViewModel, IPluginMetadata>[] OrderEditorFactory
{
get { return _orderEditorFactory; }
set
{
_orderEditorFactory = value;
Item = _orderEditorFactory.OrderByDescending(lazy => lazy.Metadata.Priority).First().Value;
}
}
private object _item;
public object Item
{
get { return _item; }
set
{
_item = value;
NotifyOfPropertyChange(() => Item);
}
}
}
Основной контроль:
<UserControl x:Class="SilverlightClassLibrary2.MainControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<StackPanel>
<TextBlock x:Name="Test" Text="Text from Core control"/>
</StackPanel>
</UserControl>
[Export(typeof (IMyViewModel))]
[ExportMetadata("Name", "Pluginc")]
[ExportMetadata("Priority", 30)]
public class MainControlViewModel : Screen, IHarnessAware, IMyViewModel
{
}
Индивидуальный контроль клиента:
<UserControl x:Class="SilverlightClassLibrary1.CustomView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<RadioButton x:Name="Test" Content="{Binding Path=Test}"/>
</Grid>
</UserControl>
[Export(typeof(IMyViewModel))]
[ExportMetadata("Name", "Plugind")]
[ExportMetadata("Priority", 2)]
public class CustomViewModel : MainControlViewModel, IHarnessAware, IMyViewModel
{
}
Интерфейс экспорта:
public interface IMyViewModel
{
}
Интерфейс экспорта метаданных:
public interface
IPluginMetadata
{
string Name { get; }
[DefaultValue(0)]
int Priority { get; }
}
Я использовал это, чтобы ответить на вопрос, потому что меня по-прежнему интересует мнение других людей, которые, возможно, уже решили подобную проблему.