簡體   English   中英

C#WPF MVVM在多個數據庫連接類型之間進行更改

[英]C# WPF MVVM change between multiple database connection types

我使用MVVM創建一個需要支持多個數據庫電機的查詢編輯器,並且想知道在視圖中切換多個數據庫連接類型的最佳做法,我正在使用C#WPF進行期末考試。

這包括有oracle,mssql,mysql連接等。

我想到了兩個這樣做的場景:

A)創建數據庫連接的新實例,在該實例中創建一個新的視圖窗口以便顯示,以便用戶可以為該特定連接工作。

B)創建一個全局訪問列表,通過書面命令在連接之間切換。 等'將數據庫更改為xxxx',用於顯示當前視圖。

我正在尋找的是場景B),因此它對用戶來說更靈活。 到目前為止,我被引導閱讀有關依賴注入和繼承的內容,它從抽象基類委托來解決這個問題。

第二件事是如何在命令字段中訪問此列表,根據寫入的數據庫名稱查找數據庫的名稱,並更改它們正在顯示的(此)當前視圖的連接類型。 但是,這需要是唯一的,因為我們無法在任何viewModels中對連接類型進行硬編碼。

目前我使用DataServices和MVVMLight nuget進行引導,每個連接類型創建一個。 在這里,我將連接存儲在一個列表中:

 public class MySqlService : IMySqlService
{
    private List<MySqlConnection> Connections = new List<MySqlConnection>();

  public MySqlConnection AddConnection(string hostName, string userName, string userPassword, string dataBase)
    {
        var connectionString = $"Server={hostName};database={dataBase};user id={userName};password={userPassword};";
        var mySqlCon = new MySqlConnection(connectionString);

        if(mySqlCon.State == ConnectionState.Closed)
        {
            mySqlCon.Open();
            Connections.Add(mySqlCon);
            return mySqlCon;
        }
        else
        {
            return null;
        }           
    }

結果案例

最佳做法是您不能動態更改數據庫類型。

在真實世界的應用程序中,您並不真正在oracle和sql server或mysql之間動態更改。 您的數據位於給定的數據庫中,而這就是它所處的位置。 這是一個很大的變化到另一個,這將需要移植數據,工作人員學習新的rdbms,很可能重寫大量的存儲過程。

某些軟件包旨在支持多個不同的rdbms,但這是在安裝之前采取的一次性決策。

一個客戶端有sql server,這就是他們想要使用的東西。 另一個客戶端擁有Oracle,因此他們期望使用它。

當然也有例外。

客戶可能希望您的小型系統在本地安裝,並通過使用免費的rdbms(如sql express)來降低成本。

通常支持一種安裝選擇。

在設計這樣的系統時,通常會嘗試最小化需要切換的系統。

這並不總是可行的。

對於簡單的系統,它有時可能只是“改變”連接字符串的問題,這由配置文件處理。

其他人有更復雜的要求,如果可能的話,傾向於封裝在存儲過程中。 這樣你的代碼可以保持不變,但Oracle有一個存儲過程,它執行oracle特定的東西和sql server數據庫有一個存儲過程做sql server特定的東西。 這意味着為每個選項編寫,測試和優化存儲過程。 這是昂貴的,遠非理想。 雖然有一個“向上”的一面。 如果客戶公司有DBA,他們可能會調整存儲過程的性能。

我在Stack.Exchange網站上找到了一個描述的響應,響應低於響應,以防它被刪除:

短:

您想要的是您的應用程序使用的接口的多個實現。

像這樣:

public interface IDatabase
{
    void SaveToDatabase();
    void ReadFromDatabase();
}

public class MySQLDatabase : IDatabase
{
   public MySQLDatabase ()
   {
      //init stuff
   }

   public void SaveToDatabase(){
       //MySql implementation
   }
   public void ReadFromDatabase(){
      //MySql implementation
   }
}

public class SQLLiteDatabase : IDatabase
{
   public SQLLiteDatabase ()
   {
      //init stuff
   }

   public void SaveToDatabase(){
       //SQLLite implementation
   }
   public void ReadFromDatabase(){
      //SQLLite implementation
   }
}

//Application
public class Foo {
    public IDatabase db = GetDatabase();

    public void SaveData(){
        db.SaveToDatabase();
    }

    private IDatabase GetDatabase()
    {
        if(/*some way to tell if should use MySql*/)
            return new MySQLDatabase();
        else if(/*some way to tell if should use MySql*/)
            return new SQLLiteDatabase();

        throw new Exception("You forgot to configure the database!");
    }
}

至於在應用程序中運行時設置正確的IDatabase實現的更好方法,您應該查看諸如“Factory Method”和“Dependancy Injection”之類的內容。


長:

這個問題,特別是在數據庫環境中,已被問過太多次了。 在這里,我將嘗試徹底向您展示使用抽象(使用接口)使您的應用程序更少耦合和更多功能的好處。

在進一步閱讀之前,如果您還不知道,我建議您閱讀並基本了解依賴注入。 您可能還想檢查適配器設計模式,這基本上是隱藏接口的公共方法背后的實現細節。

依賴注入與工廠設計模式相結合,是編寫策略設計模式的基石,也是IoC原則的一部分。

不要打電話給我們,我們會打電話給你。 (AKA好萊塢原則)。

使用抽象解耦應用程序

1.制作抽象層

如果使用C ++等語言進行編碼,則可以創建接口(或抽象類),並將泛型方法添加到此接口。 因為接口和抽象類都有你無法直接使用它們的行為,但你必須實現(在接口的情況下)或擴展(在抽象類的情況下)它們,代碼本身已經建議,你將需要具有特定的實現來完成由接口或抽象類給出的契約。

您的(非常簡單的示例)數據庫接口可能看起來像這樣(DatabaseResult或DbQuery類分別是您自己的表示數據庫操作的實現):

public interface Database
{
    DatabaseResult DoQuery(DbQuery query);
    void BeginTransaction();
    void RollbackTransaction();
    void CommitTransaction();
    bool IsInTransaction();
}

因為這是一個界面,它本身並沒有真正做任何事情。 所以你需要一個類來實現這個接口。

public class MyMySQLDatabase : Database
{
    private readonly CSharpMySQLDriver _mySQLDriver;

    public MyMySQLDatabase(CSharpMySQLDriver mySQLDriver)
    {
        _mySQLDriver = mySQLDriver;
    }

    public DatabaseResult DoQuery(DbQuery query)
    {
        // This is a place where you will use _mySQLDriver to handle the DbQuery
    }

    public void BeginTransaction()
    {
        // This is a place where you will use _mySQLDriver to begin transaction
    }

    public void RollbackTransaction()
    {
    // This is a place where you will use _mySQLDriver to rollback transaction
    }

    public void CommitTransaction()
    {
    // This is a place where you will use _mySQLDriver to commit transaction
    }

    public bool IsInTransaction()
    {
    // This is a place where you will use _mySQLDriver to check, whether you are in a transaction
    }
}

現在你有一個實現數據庫的類,界面變得有用了。

2.使用抽象層

在你的應用程序的某個地方,你有一個方法,讓我們調用方法SecretMethod,只是為了好玩,在這個方法中你必須使用數據庫,因為你想獲取一些數據。

現在你有了一個你無法直接創建的接口(呃,我該如何使用它),但你有一個MyMySQLDatabase類,它可以使用new關鍵字構建。

大! 我想使用數據庫,所以我將使用MyMySQLDatabase。

您的方法可能如下所示:

public void SecretMethod()
{
    var database = new MyMySQLDatabase(new CSharpMySQLDriver());

    // you will use the database here, which has the DoQuery,
    // BeginTransaction, RollbackTransaction and CommitTransaction methods
}

不是很好。 您正在此方法中直接創建一個類,如果您在SecretMethod中執行此操作,則可以安全地假設您將在其他30個方法中執行相同的操作。 如果要將MyMySQLDatabase更改為其他類(例如MyPostgreSQLDatabase),則必須在所有30種方法中更改它。

另一個問題是,如果MyMySQLDatabase的創建失敗,該方法永遠不會完成,因此無效。

我們首先通過將MyMySQLDatabase作為參數傳遞給方法來重構MyMySQLDatabase的創建(這稱為依賴注入)。

public void SecretMethod(MyMySQLDatabase database)
{
    // use the database here
}

這解決了您無法創建MyMySQLDatabase對象的問題。 因為SecretMethod需要一個有效的MyMySQLDatabase對象,如果發生了某些事情且該對象永遠不會傳遞給它,該方法將永遠不會運行。 這完全沒問題。

在某些應用中,這可能就足夠了。 你可能會感到滿意,但讓我們重構它會更好。

另一個重構的目的

您可以看到,SecretMethod現在使用MyMySQLDatabase對象。 假設您從MySQL遷移到MSSQL。 你真的不想改變SecretMethod中的所有邏輯,這是一個在作為參數傳遞的數據庫變量上調用BeginTransaction和CommitTransaction方法的方法,因此你創建了一個新的類MyMSSQLDatabase,它也有BeginTransaction和CommitTransaction方法。

然后繼續將SecretMethod的聲明更改為以下內容。

public void SecretMethod(MyMSSQLDatabase database)
{
    // use the database here
}

並且因為MyMSSQLDatabase和MyMySQLDatabase類具有相同的方法,所以您不需要更改任何其他內容,它仍然可以工作。

等一下!

您有一個MyMySQLDatabase實現的Database接口,您還有MyMSSQLDatabase類,它與MyMySQLDatabase具有完全相同的方法,也許MSSQL驅動程序也可以實現Database接口,因此您將它添加到定義中。

public class MyMSSQLDatabase : Database { }

但是,如果我將來不再使用MyMSSQLDatabase,因為我切換到PostgreSQL會怎樣? 我將不得不再次取代SecretMethod的定義?

是的,你願意。 這聽起來不對。 現在我們知道,MyMSSQLDatabase和MyMySQLDatabase具有相同的方法,並且都實現了Database接口。 所以你重構SecretMethod看起來像這樣。

public void SecretMethod(Database database)
{
    // use the database here
}

請注意,SecretMethod如何不再知道,您使用的是MySQL,MSSQL還是PotgreSQL。 它知道它使用數據庫,但不關心具體的實現。

現在,如果您想創建新的數據庫驅動程序,例如,對於PostgreSQL,您根本不需要更改SecretMethod。 您將創建一個MyPostgreSQLDatabase,使其實現Database接口,一旦您完成PostgreSQL驅動程序的編碼並且它可以工作,您將創建其實例並將其注入SecretMethod。

3.獲得所需的數據庫實現

在調用SecretMethod之前,您仍然必須決定所需的數據庫接口的實現(無論是MySQL,MSSQL還是PostgreSQL)。 為此,您可以使用工廠設計模式。

public class DatabaseFactory
{
    private Config _config;

    public DatabaseFactory(Config config)
    {
        _config = config;
    }

    public Database getDatabase()
    {
        var databaseType = _config.GetDatabaseType();

        Database database = null;

        switch (databaseType)
        {
        case DatabaseEnum.MySQL:
            database = new MyMySQLDatabase(new CSharpMySQLDriver());
            break;
        case DatabaseEnum.MSSQL:
            database = new MyMSSQLDatabase(new CSharpMSSQLDriver());
            break;
        case DatabaseEnum.PostgreSQL:
            database = new MyPostgreSQLDatabase(new CSharpPostgreSQLDriver());
            break;
        default:
            throw new DatabaseDriverNotImplementedException();
            break;
        }

        return database;
    }
}

正如您所見,工廠知道從配置文件中使用哪種數據庫類型(同樣,Config類可能是您自己的實現)。

理想情況下,您將在依賴注入容器中擁有DatabaseFactory。 那么您的流程可能如下所示。

public class ProcessWhichCallsTheSecretMethod
{
    private DIContainer _di;
    private ClassWithSecretMethod _secret;

    public ProcessWhichCallsTheSecretMethod(DIContainer di, ClassWithSecretMethod secret)
    {
        _di = di;
        _secret = secret;
    }

    public void TheProcessMethod()
    {
        Database database = _di.Factories.DatabaseFactory.getDatabase();
        _secret.SecretMethod(database);
    }
}

看,在創建特定數據庫類型的過程中無處可去。 不僅如此,你根本就沒有創造任何東西。 您正在依賴注入容器(_di變量)中存儲的DatabaseFactory對象上調用GetDatabase方法,該方法將根據您的配置返回正確的Database接口實例。

如果在使用PostgreSQL 3周后,您想要返回MySQL,則打開一個配置文件並將DatabaseDriver字段的值從DatabaseEnum.PostgreSQL更改為DatabaseEnum.MySQL。 你完成了。 突然,您的應用程序的其余部分通過更改一行來再次正確使用MySQL。

暫無
暫無

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

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