Ecco la situazione: Ho un assembly "comune" di accesso ai dati che contiene classi utilizzate in tutti i miei progetti. Alcuni di questi sono classi astratte che vengono implementate solo dai miei livelli di accesso ai dati per ciascun progetto.
Nei miei progetti ho un approccio a più livelli: accesso ai dati separato, livello aziendale e interfaccia utente. Le mie classi di accesso ai dati possono ereditare dalle classi astratte in Comune. Queste classi astratte contengono un metodo "execute".
Nel mio livello aziendale del progetto faccio riferimento solo al livello di accesso ai dati del progetto - Non faccio riferimento ad altri progetti o all'assemblea comune. Ma una volta che ho messo le mie classi astratte che sono sempre riutilizzate in Common, il mio livello aziendale non può più chiamare il metodo "Execute" senza avere un riferimento al comune.
Spero che non sia troppo confuso.
Se non voglio molte interdipendenze tra gli assembly, ho bisogno di spostare le classi astratte nel livello di accesso ai dati di ogni progetto. Ma poi ho ripetuto il codice e il comportamento potenzialmente incoerente tra i progetti.
Ma se continuo così com'è, tutti i miei livelli aziendali devono essere in grado di accedere a questo assembly di accesso ai dati comune, che sembra sbagliato.
Qualche idea su questa architettura?
So che alcuni potrebbero provare a dire "usa Entity Framework" o qualche altro ORM. Ma i miei progetti non sono abbastanza complessi da garantire un sovraccarico, soprattutto considerando la necessità di prestazioni veloci. Ho scoperto che un semplice framework di mia proprietà che implementa direttamente ADO.Net è notevolmente più veloce. Quindi, per favore, basta avvisarmi sulla separazione dei problemi di preoccupazione e ereditarietà e non cercare di convincermi ad aggiungere un ORM.
Codice di esempio:
In Common: classe base
Public MustInherit Class AbstractDatabaseAction
    Protected Property Factory As DbProviderFactory
    Protected Property Connection As DbConnection
    Protected Property Command As DbCommand
    Protected Property MessageForExceptions As String
    Protected Property ProviderName As String
    Protected Sub New(connString As String, providerName As String, messageForExceptions As String)
        Factory = DbProviderFactories.GetFactory(providerName)
        'set up connection
        Connection = Factory.CreateConnection
        Connection.ConnectionString = connString
        Me.ProviderName = providerName
        'set up command
        Command = Factory.CreateCommand
        Me.MessageForExceptions = messageForExceptions
    End Sub
    Public MustOverride Sub Execute()
    Protected Overridable Sub SetParameters()
        'nothing
    End Sub
    Protected MustOverride Sub SetCommandText()
    Protected Overridable Sub SetCommandType()
        Command.CommandType = CommandType.StoredProcedure
    End Sub
    Protected Sub BuildCommand()
        Command.Connection = Connection
        Me.SetCommandText()
        Me.SetCommandType()
        Me.SetParameters()
        If Me.ProviderName = "Oracle.DataAccess.Client" Then
            OracleSpecificCommandEdits()
        End If
    End Sub
    Protected Overridable Sub OracleSpecificCommandEdits()
        CType(Command, OracleCommand).BindByName = True
    End Sub
End Class
In Common: seconda classe di base (ho sia una ricerca che una versione di salvataggio, con la versione di salvataggio che consente facoltativamente le transazioni.)
Public MustInherit Class AbstractSearch
    Inherits AbstractDatabaseAction
    Protected Sub New(connString As String, providerName As String, messageForExceptions As String)
        MyBase.New(connString, providerName, messageForExceptions)
    End Sub
    Public Overrides Sub Execute()
        Try
            Me.BuildCommand()
            Using Connection
                Connection.Open()
                Using Command
                    Try
                        Dim rdr As IDataReader = Command.ExecuteReader
                        Me.fill(rdr)
                        rdr.Close()
                    Catch ex As Exception
                        Throw New Exception(MessageForExceptions & "->Search", ex)
                    End Try
                End Using
            End Using
        Catch ex As Exception
            Throw New Exception(MessageForExceptions & "->Search", ex)
        End Try
    End Sub
    Protected MustOverride Sub fill(ByRef rdr As System.Data.IDataReader)
    Protected Overrides Sub OracleSpecificCommandEdits()
        MyBase.OracleSpecificCommandEdits()
        If TypeOf (Factory) Is OracleClientFactory Then
            Dim p As DbParameter = New OracleParameter
            p.ParameterName = "results"
            p.Direction = ParameterDirection.Output
            CType(p, OracleParameter).OracleDbType = OracleDbType.RefCursor
            Command.Parameters.Add(p)
        End If
    End Sub
Protected Sub AddInParameter(key As String, value As Object)
    Dim p As IDataParameter = Command.CreateParameter
    p.Direction = ParameterDirection.Input
    p.Value = value
    p.ParameterName = key
    Command.Parameters.Add(p)
End Sub
Protected Sub AddOutParameter(key As String, type As System.Data.DbType)
    Dim p As IDataParameter = Command.CreateParameter
    p.Direction = ParameterDirection.Output
    p.DbType = type
    p.ParameterName = key
    Command.Parameters.Add(p)
End Sub
End Class
Un esempio di implementazione molto semplice di un'implementazione del livello di accesso ai dati:
Public Class IpBlackListSearch
        Inherits Common.DataAccess.AbstractSearch
        Private Property IPToSearch As String
        Public Property Results As List(Of String) = Nothing
        Public Sub New(connString As String, providerName As String, ipAddressToSearch As String)
            MyBase.New(connString, providerName, "IpAddressSearch")
            Me.IPToSearch = IPToSearch
        End Sub
        Protected Overrides Sub fill(ByRef rdr As System.Data.IDataReader)
            Results = New List(Of String)
            While rdr.Read
                Results.Add(HelperFunctions.NullScrubber(Of String)("ip"))
            End While
        End Sub
        Protected Overrides Sub SetCommandText()
            Command.CommandText = "Get_IPBlacklist"
        End Sub
        Protected Overrides Sub SetParameters()
            MyBase.AddInParameter("in_ip", Me.IPToSearch)
        End Sub
    End Class
Il problema sarebbe venuto quando nel livello aziendale del mio progetto avrei fatto qualcosa di simile:
Dim srch as new IpBlackListSearch(connstring, providername, "12.12.12.12.")
srch.execute
srch.Execute può essere compilato solo se il livello aziendale fa riferimento all'assembly di accesso ai dati comune.
Sembra dai commenti che non c'è niente di sbagliato nel mio livello aziendale contenente quel riferimento.