2014-10-27 38 views
5

我目前正在使用使用貧血域模型的代碼庫,並且我正嘗試將更多邏輯移入領域模型和域中的域模型驅動設計,但我正在努力解決以下問題。如何在存儲庫中的域模型上設置私有字段

我有一個名爲工作的域模型,它看起來像這樣,

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

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

在某個時間點上,一個工作,我想工作的最後更新日期設置爲在該特定時間點上的處理過程中。爲此,我已經爲它創建了一個公共setter,就像你上面看到的那樣。

當我從存儲庫中的數據庫中取回作業時,會出現問題,因爲我現在沒有該字段的公共設置器,因爲我已將其限制爲SetLastUpdated()

有人可以請告知我如何可以允許此屬性在存儲庫實現中設置檢索作業時,但不是從服務,它只限於調用SetLastUpdated()

更新1)我已經更新了問題,因爲使用開始日期是一個不好的例子。

更新2)從給出的答案,我可以看到這個正在做的唯一途徑是通過在資源庫中不使用AutoMapper,構建作業時添加一個構造函數到工作類設置_LastUpdated,並使用該在存儲庫的作業檢索方法中返回。

+0

你用什麼樣的模式來保溼你的域模型?你在使用ORM還是紀念品?你的構造函數是什麼樣的? – arootbeer 2014-10-27 15:16:14

+0

我正在使用AutoMapper將實體框架模型映射回領域模型。這是開始日期將被設置的地方。在領域模型上沒有構造函數,它們在設計上是貧乏的。 – Jonathan 2014-10-27 15:22:30

+0

與論壇網站不同,我們不使用「謝謝」或「任何幫助表示讚賞」,或在[so]上簽名。請參閱「[應該'嗨','謝謝',標語和致敬從帖子中刪除?](http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be - 刪除 - 從帖子)。 – 2014-10-27 15:38:55

回答

1

一種常見的方法是使用構造函數這個

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; 
    } 
} 

你不得不暴露此方法/屬性或那種方式。如果存儲庫可以設置它,您可以從其他位置進行設置。如果你試圖在這方面做出明智的決定(例如,通過使用接口),你會陷入過度工程的風險。

+0

您好oleksii,有趣的檢查使用.Kind,謝謝!我認爲使用開始日期在這個例子中是一個糟糕的主意,請檢查我的更新 – Jonathan 2014-10-27 16:27:50

+0

@Jonathan C#DateTime是個痛苦的工作,你會發現這個異常會激發很多,我們遇到了一些bug,當ORM( Automapper)創建一個新的DateTime,如果我沒有記錯的話,默認情況下這個類型是未指定的。這個對象會高興地接受utc日期,但會「忘記」設置utc類型。 – oleksii 2014-10-27 17:39:37

1

你在做DDD的主要原因之一是利用封裝提供的託管可變性。那就是總是要以構造函數開始。

通常,這是一個ORM是專門設計以執行工作的類型,但它也可以採取將已通過使用memento pattern映射到域對象的任何現有的DTO的優點:

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

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

re:AutoMapper - 這是 已經有一段時間,因爲我用它,但它應該是 possible for you to instruct it to access fields using reflection in the configuration

+0

雖然你有很好的觀點,但我沒有理解一些事情。首先,OP的代碼在日期時間沒有getter或setter領域。只有一種方法「將其設置爲utc」。所以外部代碼無法看到或設置它。因此,「當前的代碼實際上並不能阻止_startDate在將來的任何時候被更改」似乎是錯誤的。其次,備忘錄模式對於恢復對象狀態到以前的狀態是很好的。在這種情況下,我看不出它有什麼相關性。 – oleksii 2014-10-27 15:39:33

+0

@oleksii我所指的方法簽名是'public void SetStartDate()',OP已經編輯爲'public void SetLastUpdated()',我的語句不太適用。我將編輯該部分。至於「備忘錄」模式有利於將對象的狀態恢復到之前的狀態 - 如果不是對象的*前一狀態*,您認爲數據庫中的數據是什麼? – arootbeer 2014-10-27 16:12:58

+0

如果所有的工作實際上都是同一個對象 - 只是記錄同一個對象的不同狀態,那麼它可以被認爲是先前的狀態。但是一張桌子通常會存儲很多工作,這很可能不是同一份工作。紀念品存儲同一對象的突變,但它很難應用於以這種方式存儲不同對象的突變。合理? – oleksii 2014-10-27 16:23:51

2

正如我所見,您有各種選擇。

選項1

假設你的資料庫有兩種方法:

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

,您可以給Job類兩個構造,即需要一個DateTime和一個不。

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

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

    } 

    public DateTime StartDate { get; private set; } 
} 

這並不妨礙該服務呼叫「錯誤」的構造,但至少它傳達稱它沒有startDate給調用者的選項。

選項2

兩個不同的Job類的工作。

你的倉庫可能看起來像這個:

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

而且NewJob類可能看起來像:

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

    public DateTime StartDate { get; private set; } 
} 

該通信意圖更好,因爲該倉庫的Create方法只接受NewJob實例因此該模型的用戶將被迫創建NewJob而不是Job

選項3

忽略在repostory的Create方法StartDate,並始終在方法中它設置爲DateTime.UtcNow。甚至可以在設置它的數據庫中創建一個Insert觸發器。

+0

感謝您回答克勞斯。我認爲在示例中使用開始日期是一個糟糕的主意。檢查我的更新。 – Jonathan 2014-10-27 16:23:18

相關問題