VB.NET Entity Framework 使用多个 DbContext 访问多个数据库服务器 类

VB.NET Entity Framework access multiple database server with several DbContext classes

我有一个测试数据库和一个实时数据库位于不同的服务器上。我目前的设计设法与两者一起工作,但我想改进访问。我现在没主意了,想听听你的意见。


问题

如何解决以下设计问题?

我创建了两个 EF6 classes 来访问不同的数据库。 (突出显示的)

在程序启动时,我根据构建模式定义了一个默认连接。

#If DEBUG Then
        myDbType = AbstractDBAccess.DatabaseType.Test
#Else
        myDbType = AbstractDBAccess.DatabaseType.Live
#End If

然后我使用 myDBType 创建 DBAccess 对象来与我的数据库交互。从现在开始,它会自动注意连接到测试或实时数据库。

Dim userAccess = new UserDBAccess(myDBtype)
userAccess.GetUser()
userAccess.Dispose()

Dim projectAccess = new ProjectDBAccess(myDBType)
projectAccess.DoWork()
projectAccess.Dispose()

我在 youtube 上观看了 SW-Architecture 视频后有了这个想法 https://www.youtube.com/watch?v=sA-Hp4aBWb4 我根据自己的需要进行了修改。

到目前为止,这看起来是一种非常干净的方法,但我 运行 遇到了麻烦。

我的问题是,对于每次数据库访问,我必须 copy/paste 99% 的代码,具体取决于我要访问的服务器。例如

对于实时数据库:ctxLive < @see below code

对于测试数据库:ctxTest < @see below code

我有一个基础 class,所有 DBAccess classes 都从中派生。

Imports System.Data.Entity
Public MustInherit Class AbstractDBAccess
    Implements IDisposable
#Region "Fields"
    ' Access live db via EF 6
    Protected ctxLive As DBLiveEntities

    ' Access test db via EF 6
    Protected ctxTest As DBTestEntities

    ' Remember DB to access
    Protected myDBType As DatabaseType
#End Region
#Region "Enum"
    ''' <summary>
    ''' Add more data bases here.
    ''' </summary>
    ''' <remarks>Matthias Köhler</remarks>
    Public Enum DatabaseType
        Live = 0
        Test = 1
    End Enum
#End Region
#Region "Constructor"
    Public Sub New(ByVal dbType As AbstractDBAccess.DatabaseType)

        myDBType = dbType ' 

        ' Depending on what type we get from startup we grant access to test or live DB
        Select Case dbType
            Case DatabaseType.Live
                Me.ctxLive = New DBLiveEntities

            Case DatabaseType.Test
                Me.ctxTest = New DBTestEntities
        End Select
    End Sub
#End Region
#Region "Methods"
    Public Function GetDBAccess() As DbContext
        ' My Problem is i need to return two different types in this method.
        ' After creating an instance I save which access this object was intended for with "myDBType"
        ' Both classes derive from DbContext but if I implement it this way I can't see my tables. See screenshot below.
        Select Case myDBType
            Case DatabaseType.Live
                Return Me.ctxLive
            Case DatabaseType.Test
                Return Me.ctxTest
        End Select

        Throw New Exception("No matching case for " & myDBType.ToString)
    End Function
#End Region

问题

你看Select-大小写是 99% 相同的。想象一下复杂的代码和 15 classes。我只是不喜欢那种复制粘贴。我只需要更改 "ctxLive" 或 "ctxTest".

想象一下,有人必须在某些年内添加另一个数据库。他必须检查整个代码并为每个方法添加一个案例。

有没有更好的方法?

此处是此屏幕截图的匹配代码。

Public Class UserDBAccess
    Inherits AbstractDBAccess
    Implements IDisposable

    Public Sub New(ByVal dbType As AbstractDBAccess.DatabaseType)
        MyBase.New(dbType)
    End Sub

    Public Sub GetUser()
        ' Currently I have to add a lot of select cases to seperate my live DB and test DB.
        ' They have different connection strings and are on different servers
        Select Case Me.myDBType
            Case DatabaseType.Live
                Me.ctxLive.CCTUsers.Where(Function(u) u.UserName.Contains("Whosebug"))
            Case DatabaseType.Test
                Me.ctxTest.CCTUsers.Where(Function(u) u.UserName.Contains("Whosebug"))
        End Select

        ' I have a lot of Copy Pasting which in my opinion is ugly.
        ' I want sth like this to save me all that select cases
        ' The difference here is the "GetDBAccess" 
        Me.GetDBAccess.CCTUsers.Where(Function(u) u.UserName.Contains("Whosebug"))
    End Sub
End Class

以下解决方案绝对更干净,将来更易于维护。

将连接字符串添加到您的 app.config

<connectionStrings>
    <add name="DB_Live" connectionString="liveDB" providerName="System.Data.EntityClient" />
    <add name="DB_Test" connectionString="testDB" providerName="System.Data.EntityClient" />
</connectionStrings>

创建一个 DEBentityManager

Public Class DBEntityManager
Inherits DbContext

Public Sub New(ByVal connString As String)
    MyBase.New(connString)
End Sub

Public Overridable Property MyTable() As DbSet(Of MyTable)

End Class

然后我添加了对 ConfigurationManager 的引用:

  1. 右键单击您的项目(不是解决方案)
  2. 添加 -> 引用 ... -> 框架
  3. 搜索 "Configuration" 并激活 "System.Configuration"
  4. 的复选框
  5. 点击"OK"

启动时,我根据构建模式配置连接字符串

#If DEBUG Then
        connString = Configuration.ConfigurationManager.ConnectionStrings("DB_Test").ConnectionString()
#Else
        connString = Configuration.ConfigurationManager.ConnectionStrings("DB_Live").ConnectionString()
#End If

我重构了我的基础 class 看起来像这样。

它现在获取连接字符串并创建 DBEntityManager 对象。 DBEntityManager 派生自 DBContext 并使用传递的连接字符串。

Public Class DBAccessAbstract
    Implements IDisposable

#Region "Field Declaration"
    Protected ctx As DBEntityManager
#End Region

#Region "Constructors"
    Public Sub New(ByVal connString As String)
        ctx = New DBEntityManager(connString)
    End Sub
#End Region

#Region "IDisposable Support"
    Private disposedValue As Boolean ' To detect redundant calls

    ' IDisposable
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                Me.ctx.Dispose()
            End If

            ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
            ' TODO: set large fields to null.
        End If
        Me.disposedValue = True
    End Sub

    ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
    'Protected Overrides Sub Finalize()
    '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    '    Dispose(False)
    '    MyBase.Finalize()
    'End Sub

    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class

我为每个 table 创建了我想要的访问器

Public Class DBAccessUserTable
Inherits DBAccessAbstract
Implements IDisposeable

Public Sub New(ByVal connString as String)
MyBase.New(connString)
End Sub
  Public Function Exists(ByVal userName As String) As Boolean
    Dim user As UserTable

        user = Me.ctx.UserTables.Where(Function(e) e.UserName.Contains("Whosebug")).FirstOrDefault
        If IsNothing(user) Then Return False
        Return True
  End Function
End Class

现在我可以通过创建我的 DBAccess class

的新实例来自动访问测试或实时数据库
Dim dbEmpl As New DBAccessUserTable(Me.connString)
            If Not dbEmpl.Exists(userName) Then Throw New System.Exception(userName & " doesn't exist.")
            MessageBox.Show("True!")
            dbEmpl.Dispose()

谢谢你给我出主意!