我目前正在为 3 层解决方案寻找一个好的项目结构,以避免在新项目中进行不必要的工作。
该项目将包括一个核心产品,该产品具有
- 包含 EF 代码优先模型的模型项目
- 在服务器上有业务逻辑和通信逻辑的项目
- 客户端上的存储库项目
- 带有视图和视图模型的 silverlight 项目(想在这里使用 caliburn.micro)
现在的问题是客户可能有一些特殊要求,这可能导致上述所有项目发生变化。所以我的想法是我可以只使用基本结构并为客户创建相同的结构。如果没有更改,我将只有空类,它们只是扩展基类并且什么都不添加。
这给我带来了以下问题:
- 实体框架(代码优先)中的一个问题是在一个项目中拥有基类(已经功能齐全)并且有另一个项目可以使用新字段扩展模型类吗?
- 更改用户控件是否存在 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 结合使用),这在这里很有帮助。
我将创建包含 ContentControl 元素的 UserControl,这些元素是使用 MEF 动态获取的。这意味着我又拥有了一个包含所有视图和 ViewModel 的核心项目。如果我需要在某处为客户交换特殊控件,我将控件更改为 ContentControl 并为其创建核心视图和 ViewModel(与更改请求之前相同)。ContentControl 的这个 ViewModel 带有一个导出接口和 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; }
}
我确实用它来回答这个问题,因为我仍然对可能已经解决了类似问题的其他人的意见感兴趣。