2014-10-27 2 views
5

Attualmente sto lavorando con un codebase che utilizza il modello di dominio anemico e sto cercando di spostare più logica nei modelli di dominio in uno spostamento verso un modello di dominio e un dominio Driven Design, ma sono alle prese con il seguente problema.Come impostare campi privati ​​su un modello di dominio nel repository

Ho un modello di dominio chiamato lavoro che assomiglia a questo,

public class Job 
{ 
    private DateTime _someOtherDate; 
    private DateTime _lastUpdated; 

    // this may be called from many different services 
    public void SetLastUpdated() 
    { 
    _lastUpdated = DateTime.UtcNow; 
    } 
} 

Ad un certo punto nel tempo, durante l'elaborazione di un lavoro che desidera impostare data dell'ultimo aggiornamento del lavoro a quel punto specifico nel tempo. Per fare questo ho creato un setter pubblico per questo come puoi vedere sopra.

Un problema sorge quando sto ritirando il lavoro dal database nel mio repository, poiché ora non ho un setter pubblico per questo campo perché l'ho limitato a SetLastUpdated().

Qualcuno può consigliare come consentire a questa proprietà di essere impostata nell'implementazione del repository quando si recupera il lavoro, ma non dal servizio in cui è limitato alla chiamata SetLastUpdated().

Aggiornamento 1) Ho aggiornato la domanda poiché la data di inizio era un cattivo esempio.

Update 2) Dalle risposte fornite, l'unico modo che posso vedere questo viene fatto è non usando automapper nel repository, l'aggiunta di un costruttore sulla classe di lavoro per l'impostazione _lastUpdated, e l'utilizzo di questo quando la costruzione del lavoro da restituire nel metodo di recupero del lavoro del repository.

+0

Che tipo di schema stai utilizzando per idratare i tuoi modelli di dominio? Stai usando un ORM, o ricordi? Come sono i tuoi costruttori? – arootbeer

+0

Sto usando AutoMapper per mappare i modelli di Entity Framework ai modelli di dominio. Qui è dove verrà impostata la data di inizio. Non ci sono costruttori sui modelli di dominio, sono anemici di design. – Jonathan

+0

A differenza dei siti di forum, non utilizziamo "Grazie" o "Qualsiasi aiuto apprezzato" o firme su [so]. Vedi "[Se 'Hi', 'thanks', tagline e saluti saranno rimossi dai post?] (Http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be -removed-from-posts) –

risposta

1

Un approccio comune è quello di utilizzare i costruttori di questo

public class Job 
{ 
    private DateTime _startDate; 

    public void Job() 
    { 
    _startDate = DateTime.UtcNow; 
    } 

    public void Job(DateTime dt) 
    { 
    // check DateTime kind, this could be source of a bug 
    if(dt.Kind != DateTimeKind.Utc) throw new ... 
    _startDate = dt; 
    } 
} 

Bisogna esporre questo metodo/proprietà un modo o nell'altro. Se il repository può impostarlo, è possibile impostarlo da altre posizioni. Se si cerca di essere intelligenti su questo (si potrebbe, ad esempio, utilizzando le interfacce), si rischierebbe di sovrastimare.

+0

Ciao oleksii, controllo interessante usando .Kind, grazie per quello! Penso che usando la data di inizio nell'esempio è stata una cattiva idea Controlla il mio aggiornamento – Jonathan

+0

@Jonathan C# DateTime è un rompicapo con cui lavorare. Vedrai che questa eccezione sarà molto forte. Abbiamo avuto alcuni bug con questo. Automapper) crea un nuovo DateTime, per impostazione predefinita il tipo non è specificato, se non ricordo male.Questo oggetto accetterà felicemente la data di utc, ma "dimenticherà" per impostare il tipo di utc – oleksii

1

Uno dei motivi principali per cui si sta facendo DDD è sfruttare la mutabilità gestita che può essere fornita dall'incapsulamento. Questo è sempre che inizia con il costruttore.

In generale, questo è il tipo di lavoro un ORM è specificamente progettato per eseguire, ma può anche trarre vantaggio da eventuali DTOs esistenti che sarebbero stati mappati sull'oggetto dominio utilizzando il memento pattern:

public Job() { 
    _lastUpdated = DateTime.UtcNow; 
    // ... 
} 

public Job(JobData data) { 
    _lastUpdated = data.LastUpdated; 
    //... 
} 

re: AutoMapper - è da un po 'di tempo da quando l'ho usato, ma dovrebbe essere possible for you to instruct it to access fields using reflection in the configuration.

+0

Mentre tu hai punti buoni, io non capisco un paio di cose: in primo luogo, il codice OP non ha getter o setter per la data campo. C'è solo un metodo per "impostarlo su utc ora". Quindi il codice esterno non può vederlo o impostarlo. Quindi "il codice corrente non impedisce in realtà che _startDate venga modificato in qualsiasi momento futuro" sembra essere errato. In secondo luogo, lo schema del ricordo è utile per ripristinare lo stato di un oggetto rispetto agli stati precedenti. Non riesco a vedere come sia rilevante in questo contesto. – oleksii

+0

@oleksii La firma del metodo a cui mi riferivo era "public void SetStartDate()", che l'OP ha modificato in "public void SetLastUpdated()", a cui la mia affermazione è molto meno applicabile. Modificherai quella parte. Per quanto riguarda il pattern 'memento' che è buono per ripristinare lo stato di un oggetto ai suoi stati precedenti - quali considerano i dati nel database se non lo * stato precedente * dell'oggetto? – arootbeer

+0

Potrebbe essere considerato stato precedente, se tutti i lavori fossero, di fatto, lo stesso oggetto - semplicemente registrando stati diversi dello stesso oggetto. Ma una tabella di solito memorizza molti lavori, che molto probabilmente non sono lo stesso lavoro.Memento memorizza le mutazioni dello stesso oggetto, ma difficilmente può essere applicato per memorizzare le mutazioni di oggetti diversi in questo modo. Ha senso? – oleksii

2

Come vedo, ci sono varie opzioni.

Opzione 1

Supponendo che il repository ha due metodi:

public IEnumerable<Job> ReadAll() { ... } 
public int CreateJob(Job job) { ... } 

si può dare la classe Job due costruttori, uno che prende uno DateTime e che non.

public class Job 
{ 
    public Job(DateTime startDate) 
    { 
     this.StartDate = startDate; 
    } 

    public Job() : this(DateTime.UtcNow) 
    { 

    } 

    public DateTime StartDate { get; private set; } 
} 

Ciò non toglie che il servizio di chiamare il costruttore "sbagliato", ma almeno comunica la possibilità di chiamare senza il startDate al chiamante.

Opzione 2

Lavora con due differenti Job classi.

Il repository potrebbe apparire come questo, invece:

public IEnumerable<Job> ReadAll() { ... } 
public int CreateJob(NewJob newJob) { ... } 

E la classe NewJob potrebbe apparire come:

public class NewJob 
{ 
    public NewJob() 
    { 
     this.StartDate = DateTime.UtcNow; 
    } 

    public DateTime StartDate { get; private set; } 
} 

Questo comunica l'intenzione migliore, perché il metodo del repository Create accetta solo un'istanza di NewJob quindi l'utente del modello sarà costretto a creare uno NewJob anziché uno Job.

Opzione 3

Ignora la StartDate nel metodo di repostory Create e sempre impostarlo su DateTime.UtcNow all'interno del metodo. O addirittura arrivare a creare un trigger Insert nel database che lo imposta.

+0

Grazie per aver risposto Klaus. Penso che usare la data di inizio nell'esempio sia stata una cattiva idea. Controlla il mio aggiornamento. – Jonathan