Es gibt zwei gebräuchliche Möglichkeiten, um eine flüssige Schnittstelle zu erstellen.
Eine Möglichkeit besteht darin, der aktuellen Instanz der Klasse, die gebaut wird, hinzuzufügen und this
von jeder Methode zurückzugeben.
Etwas wie folgt aus:
public class NamesBuilder
{
private List<string> _names = new List<string>();
public NamesBuilder AddName(string name)
{
_names.Add(name);
return this;
}
}
Das Problem mit dieser Art von Builder ist, dass Sie fehlerhaftem Code einfach schreiben:
var namesBuilder = new NamesBuilder();
var namesBuilder1 = namesBuilder.AddName("John");
var namesBuilder2 = namesBuilder.AddName("Jack");
Wenn ich diesen Code sah ich erwarten würde, dass namesBuilder1
und namesBuilder2
würde jeder nur einen Namen haben, und dass namesBuilder
keine haben würde. Die Implementierung würde jedoch beide Namen in allen drei Variablen haben, da sie dieselbe Instanz sind.
Der bessere Weg zur Implementierung einer fließenden Oberfläche besteht darin, eine Kette von Builder-Klassen zu erstellen, die langsam ausgewertet werden, sodass Sie die letzte Klasse erstellen können, sobald Sie fertig sind. Wenn Sie dann mitten in den Bauprozess verzweigen, können Sie einen Fehler machen.
Hier ist die Art von Code zu schreiben, ich erwarten würde:
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();
Der Code, diese Arbeit zu machen, ist ein wenig komplizierter als die „Namen“ Beispiel.
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;
}
}
}
Der andere Vorteil dieses Builder-Typs besteht darin, dass Sie auch stark typisierte Eigenschaftsfelder verwalten.
Natürlich müssen Sie den richtigen Code für Ihr Mapping in der Create
Methode eingeben.
Wie sieht die Benutzeroberfläche aus, die Sie jetzt haben? Was war das spezifische Problem, das Sie davon abgehalten hat, es so aussehen zu lassen, wie Sie es wollten? –
Ich konnte nicht herausfinden, wie man die generische Inferenz vollständig auf die .Properties() -Methode herunterzieht, ohne beide Typen gleichzeitig zu übergeben. Die Art, wie ich das machen möchte, besteht darin, jedes generische Element einzeln zu übergeben und dann in der Property() -Methode zusammenzuführen. –
Klingt so, als ob Sie ein Array von Typen benötigen. Hast du einen Blick auf [Automapper?] Geworfen (https://github.com/AutoMapper/AutoMapper) –