我想创建一个流畅的界面,可以这样使用:
void Main() {
ModelStateMappings.MapDomainModel<Book>().MapViewModel<BookViewModel>()
.Properties(book => book.Author, vm => vm.AuthorsName)
.Properties(book => book.Price, vm => vm.BookPrice);
ModelStateMappings.MapDomainModel<Store>().MapViewModel<StoreViewModel>()
.Properties(store => store.Owner, vm => vm.OwnersName)
.Properties(store => store.Location, vm => vm.Location);
}
我最终想要一个看起来像这样的集合:
static class ModelStateaMappings {
private static IList<ModelMappings> mappings;
// other methods in here to get it working
}
class ModelMappings {
public Type DomainModelType {get;set;}
public Type ViewModelType {get;set;}
public IList<PropertyMapping> PropertyMappings {get;set;}
}
class PropertyMapping {
public Expression<Func<object, object>> DomainProperty {get;set;}
public Expression<Func<object, object>> ViewModelProperty {get;set;}
}
我无法获得上述功能,但I did create something similar以类似的方式工作,但我并不特别喜欢如何设置流畅的界面。我宁愿让它读起来像我上面的方式。
答案 0 :(得分:4)
有两种常用方法可以创建流畅的界面。
一种方法是添加到正在构建的类的当前实例,并从每个方法返回this
。
这样的事情:
public class NamesBuilder
{
private List<string> _names = new List<string>();
public NamesBuilder AddName(string name)
{
_names.Add(name);
return this;
}
}
这种构建器的问题在于您可以轻松编写错误代码:
var namesBuilder = new NamesBuilder();
var namesBuilder1 = namesBuilder.AddName("John");
var namesBuilder2 = namesBuilder.AddName("Jack");
如果我看到此代码,我希望namesBuilder1
和namesBuilder2
每个只有一个名称,而namesBuilder
则没有。但是,实现在所有三个变量中都有两个名称,因为它们是同一个实例。
实现流畅接口的更好方法是在延迟评估的构建器类上创建一个链,以便在构建完成后创建最终类。然后,如果你在构建过程中分支,你可能会犯错误。
以下是我希望编写的代码类型:
var bookMap =
ModelStateMappings
.Build<Book, BookViewModel>()
.AddProperty(book => book.Author, vm => vm.AuthorsName)
.AddProperty(book => book.Price, vm => vm.BookPrice)
.Create();
var bookStore =
ModelStateMappings
.Build<Store, StoreViewModel>()
.AddProperty(store => store.Owner, vm => vm.OwnersName)
.AddProperty(store => store.Location, vm => vm.Location)
.Create();
使这项工作的代码比“名称”示例稍微复杂一些。
public static class ModelStateMappings
{
public static Builder<M, VM> Build<M, VM>()
{
return new Builder<M, VM>();
}
public class Builder<M, VM>
{
public Builder() { }
public Builder<M, VM> AddProperty<T>(
Expression<Func<M, T>> domainMap,
Expression<Func<VM, T>> viewModelMap)
{
return new BuilderProperty<M, VM, T>(this, domainMap, viewModelMap);
}
public virtual Map Create()
{
return new Map();
}
}
public class BuilderProperty<M, VM, T> : Builder<M, VM>
{
private Builder<M, VM> _previousBuilder;
private Expression<Func<M, T>> _domainMap;
private Expression<Func<VM, T>> _viewModelMap;
public BuilderProperty(
Builder<M, VM> previousBuilder,
Expression<Func<M, T>> domainMap,
Expression<Func<VM, T>> viewModelMap)
{
_previousBuilder = previousBuilder;
_domainMap = domainMap;
_viewModelMap = viewModelMap;
}
public override Map Create()
{
var map = _previousBuilder.Create();
/* code to add current map to Map class */
return map;
}
}
}
此类构建器的另一个优点是您还可以维护强类型属性字段。
当然,您需要在Create
方法中为您的映射添加正确的代码。
答案 1 :(得分:2)
您可以使用以下代码
来实现它static class ModelStateMappings
{
public static DomainModelMapping<TDomainModel> MapDomainModel<TDomainModel>()
{
// edit the constructor to pass more information here if needed.
return new DomainModelMapping<TDomainModel>();
}
}
public class DomainModelMapping<TDomainModel>
{
public ViewModelMapping<TDomainModel, TViewModel> MapViewModel<TViewModel>()
{
// edit the constructor to pass more information here if needed.
return new ViewModelMapping<TDomainModel, TViewModel>();
}
}
public class ViewModelMapping<TDomainModel, TViewModel>
{
public ViewModelMapping<TDomainModel, TViewModel>
Properties<TDomainPropertyType, TViewModelPropertyType>(
Expression<Func<TDomainModel, TDomainPropertyType>> domainExpr,
Expression<Func<TViewModel, TViewModelPropertyType>> viewModelExpr)
{
// map here
return this;
}
}
您不必指定所有先前设置的泛型类型,因为它们已被记住为返回类型的通用参数。可以跳过Properties
方法调用的通用参数,因为它们将由编译器推断。与在任何地方使用object
相比,你获得了更好的打字。
当然这是最简单的版本。您可以在这些类型之间传递更多信息,因为您指定了下一个必要类型的创建方式。
它还调用MapViewModel
而不首先调用MapDomainModel
(只要你创建构造函数internal
并关闭所有单独的dll中的所有东西),这应该是一件好事。< / p>