簡體   English   中英

將實體保存到數據庫的設計模式

[英]Design pattern for saving entities into database

我有一個類似於下面的類(C#):

public class Product {

    public int ID {get;set;}
    public string Name {get;set;}
    public double Price {get;set;}

    public void Save() {
        string sql = "INSERT INTO Product.....";
        Database.Execute(sql);
    }

    public void Delete() {
        string sql = "DELETE Product WHERE.....";
        Database.Execute(sql);
    }
}

我主要擔心的是上面的代碼違反了SOLID原則,因為它負責創建和刪除自身。

也許這些Save和Delete方法應該放在Product實體之外的某個地方(Factory / Repository可能?)。

我相信Facade模式在你的情況下會做得很好。 Facade模式也稱為服務層。

在你的情況下,你基本上會有一個服務(一個類),它將擁有你需要的所有方法。 您的服務應該是這樣的。

class ProductService 
{
    public void Save(Product product)
    {
       // SAVE THE PRODUCT
    }

    public void Delete(Product product)
    {
        // DELETE PRODUCT
    }
}

您希望將類注入要保存或刪除產品的位置。 這樣,您所要做的所有工作都將在一個單獨的類中,您的代碼將變得更加清晰。 讓所有這些在存儲過程中插入和刪除statemenet也是一個好主意。

我將介紹您的模型實體,命令和查詢模式以及數據庫層或存儲庫。

您的模型是您的Product ,此對象應該是一個普通對象:

public class Product : IEntity {
    public int ID { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
}

接下來,我將創建一個用於處理此實體的命令和查詢接口:

public interface ICommand {} // Marker interface

public interface IQuery<TResult> {} // Marker interface

接下來為ICommandIQuery定義處理程序:

public interface IHandleQuery<TQuery, TResult> where TQuery : IQuery<TResult> 
{
    TResult Handle(TQuery query);
}

public interface IHandleCommand<TCommand> where TCommand : ICommand
{
    void Handle(TCommand command);
}

現在,您可以清楚地指出並分離您的寫入(命令)和讀取(查詢)方面。

這意味着我們可以創建一個命令及其處理程序來保存您的Product如:

public class SaveProduct : ICommand 
{
    public string Name { get; private set; }
    public double Price { get; private set; }

    public SaveProduct(string name, double price) 
    {
        Name = name;
        Price = price;
    }
}

public class HandleSaveProduct : IHandleCommand<SaveProduct> 
{
    private readonly IRepository<Product> _productRepository;

    public HandleSaveProduct(IRepository<Product> productRepository) 
    {
        _productRepository = productRepository;
    }

    public void Handle(SaveProduct command) 
    {
        var product = new Product {
            Name = command.Name,
            Price = command.Price
        };

        _productRepository.Save(product);
    }
}

在上面我們已經定義了一個用於處理這個實體的存儲庫,但是你可以在這里直接依賴你的數據庫上下文並對它執行查詢/命令,或者你可以使用GenericRepository<TEntity> : IRepository<TEntity>來實現存儲庫模式。只是單獨的產品庫:

public interface IEntity { } // Marker interface

public interface IRepository<TEntity> where TEntity : IEntity 
{
    TEntity Get(object primaryKey);

    void Save(TEntity entity); // should handle both new and updating entities

    void Delete(TEntity entity);

}

public class ProductRepository : IRepository<Product> 
{
    public Product Get(object primaryKey) 
    {
        // Database method for getting Product
    }

    public void Save(Product entity) 
    {
        // Database method for saving Product
    }

    public void Delete(Product entity) 
    {
        // Database method for deleting Product
    }
}

您永遠不應該將您的Product實體返回到您的UI,而是使用視圖模型,例如:

public class ProductViewModel {
    public int ID { get; set; }
    public string Name { get; set; }
    public double Price { get; set; }
    public DateTime Whatever { get; set; }
}

public class GetProductById : IQuery<ProductViewModel>
{
    public int Id { get; private set; }

    public GetProductById(int id)
    {
        Id = id;
    }
}

public class HandleGetProductById : IHandleQuery<GetProductById, ProductViewModel>
{
    private readonly IRepository<Product> _productRepository;

    public HandleGetProductById(IRepository<Product> productRepository) 
    {
        _productRepository = productRepository;
    }

    public ProductViewModel Handle(GetProductById query)
    {
        var product = _productRepository.Get(query.Id);
        return product.Select(x => new ProductViewModel {
            Name = x.Name,
            Price = x.Price;
        });
    }
}

請注意這是用記事本寫的,可能無法100%編譯,但你應該知道如何分離各種組件以便遵循SOLID。 :-)

你似乎想要某種類似Repository的存儲庫 你已經在[問題]中提到了它。 該鏈接僅供參考 - 我建議您實施。 為什么?

因為像@Igor說的那樣,如果你使用的是ORM,那么你將免費獲得這份合同。 例如,NHibernate有一個帶有Query<T>()Save()Delete()等方法的ISession 這就是你所需要的。

我使用的幾乎所有項目都使用了這種“基礎設施”ORM合同的抽象(服務/存儲庫/等),所謂的抽象很弱,只能創造更多的代碼來維持和更高的技術債務和錯誤。

采取務實的方法:

  • 不要通過內部的ADO.NET調用創建自己的Repository / ORM抽象來重新發明輪子。 使用像Fluent NHibernate這樣的實體ORM,這使得映射變得簡單並且易於與數據交互(其他完美的替代方案可能是實體框架等)。 如果這對你來說太多了,試試像Dapper這樣非常簡單的東西 - 它是一個非常輕量級的ORM,可以映射到你的模型,就像怪異的魔法一樣,你仍然可以編寫所有自己的SQL。 您將獲得使用的ORM合約界面,我相信這是您在這里所要求的,並且您可以繼續構建您的應用程序,而不是考慮過度工程。
  • 通過在控制器中使用ORM合同來保持簡單('控制器'不必是MVC控制器,它可以是應用程序的UI入口點。記住:避免不必要的抽象)。 這是一些簡單的例子。
  • 人們希望保持干燥 ,但開發人員必須使用#reusingallthethings的奇怪成癮意味着他們經常擁有存儲庫或服務,它們包含完美的ORM合同電話,並且通常只有一兩次使用。 忘了重用! 使用規則三 ,首先查詢,保存和刪除控制器中的邏輯,並在您知道需要時僅提取可重用的代碼。
    我知道這些例子是微不足道的 ,但只是想象你需要返回一些數據,查詢需要一些長linq表達式或帶有連接的復雜選擇。 現在想象你在幾個地方需要這個相同的查詢(它發生。不經常,但你會有一些) - 復制並粘貼它! 恩,那就對了; 你不敢相信我說過,但我做到了。 將相同的10行復制並粘貼到代碼中的2,3或4個位置。 這完全沒問題。 沒有人會死。 只要linq表達式本身( GetTop15TransactionsWithoutFeesExcludingCreditsGroupByDayRecentAtTop()任何人?),您不需要查詢對象或Repository方法。

HTH。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM