Per favore, puoi dirmi se il mio modello di accesso ai dati ha senso?
Grazie mille.
Sfortunatamente, poiché hai pubblicato un'immagine, piuttosto che un codice, è più difficile fare riferimento al tuo design quando lo commenta.
Per me, il punto in cui il tuo design va tutto storto è:
Public DAL(int DbType)
switch(DbType ...
Non rendi DAL
reponsible per creare un'implementazione concreta di AbstractDAL
oltre ad essere un'implementazione di IFootballProject
. Questo mina tutto il lavoro accurato che il resto della tua immagine mette in cose astratte. Ricorda, racconta, non chiedere. E ricorda il principio della singola responsabilità. Iniettare il DAL concreto (creato dal codice di configurazione dell'app, ad esempio il contenitore IoC) in un'implementazione di IFootballProject
Ho appena notato che questa è una domanda in C #. In tal caso, non creare AbstractDAL
una classe base astratta. Falla un'interfaccia. Per favore.
@David La risposta di Arno è IMHO bene. Tuttavia, quando si aggiusta la classe DAL
, ci sono altri dettagli da considerare:
il nome della classe non dovrebbe essere DAL
, probabilmente dovrebbe essere FootballProject
. Questo puzza ancora di diventare una classe di Dio, ma va bene iniziare da esso e refactor quando diventa più grande.
lo switch nel codice costruttore appartiene da qualche altra parte. Potrebbe essere refactored ad alcuni factory class e then il codice di setup dell'applicazione può usarlo per creare un'implementazione specifica di un DAL astratto.
il switch
non dovrebbe usare int
, dovrebbe usare un enum, dove i valori hanno nomi propri.
L'ultima volta che ho scritto un DAL è stato simile a questo:
// Season data, stored in a Season table
class Season
{
public static List<Season> GetAllSeasons(DataReader dataReader);
// plus public season properties and/or methods
}
My DataReader è come il tuo AbstractDAL e implementa i metodi di accesso al database. Contiene (cioè avvolge, incapsula) un DbConnection che potrebbe essere un SqlConnection o qualcos'altro.
Il metodo GetAllSeasons (ad esempio) lo utilizza per leggere le istanze di Seasons dalla tabella corrispondente.
Aggiungo nuovi tipi di dati aggiungendo nuove classi (ad es. Paese) senza modificare le classi esistenti.
Il codice dell'applicazione potrebbe usarlo in questo modo:
using (DataReader dr = new DataReader())
{
List<Season> seasons = Season.GetAllSeasons(dr);
}
L'applicazione controlla la durata delle istanze DataReader (e DataWriter).
Si noti che DataReader e DataWriter sono di breve durata, facili da usare (basta crearlo senza argomenti del costruttore e passarlo come parametro ai metodi statici di classi di dati come Season che sanno come usarli) e implementare IDisposable.
In C # puoi definire i metodi di estensione statici. Quindi qualcosa come:
static class SeasonExtensions
{
public static List<Season> GetAllSeasons(this DataReader dataReader)
{
return Season.GetAllSeasons(dataReader);
}
}
... "estende" la classe DataReader in modo che l'applicazione possa utilizzarla in questo modo:
using (DataReader dr = new DataReader())
{
List<Season> seasons = dr.GetAllSeasons();
}
Ho scritto, "il mio DataReader è come il tuo AbstractDAL" ... ma il mio DataReader non è astratto: è una sottoclasse di foglie che l'applicazione può istanziare facilmente.
La misura in cui è astratto è una gerarchia di classi come questa:
// base class, implements methods for reading
abstract class DataBase { ... }
// subclass, inherits methods for reading
sealed class DataReader : DatabaseBase { ... }
// subclass, adds methods for writing
sealed class DataWriter : DataBase { ... }
L'applicazione decide se creare un'istanza di un DataReader o di un DataWriter: a seconda che sia necessario eseguire una scrittura durante questa transazione o solo una lettura.
Quindi la classe per un tipo specifico di dati (ad esempio Season) definisce metodi come:
class Season
{
// can be called with DataReader or DataWriter
public static List<Season> GetAllSeasons(DataBase dataBase);
// can only be called with a DataWriter
public void InsertNew(DataWriter dataWriter)
// plus public season properties and/or methods
}
No. Il bello è il tuo IFootballProject.
Basta implementarlo due volte, una volta per tipo di database. Non cercare di astrarre l'accesso al database che non è necessario e qualcosa come mongoDB non si adatta alle astrazioni sql
Grazie a tutti per i vostri commenti.
Sto imparando queste cose, quindi a volte il tuo vocabolario è difficile da capire per me, ma per quello che potrei dedurre dai commenti.
Qualcuno ha detto che la mia classe base AbstractDAL
dovrebbe essere un'interfaccia, ma ho alcune implementazioni che saranno trasversali per tutte le sottoclassi che potrebbero derivarne.
public abstract class AbstractDAL
{
protected DbCommand Command { get; set; }
protected DbConnection Connection { get; set; }
protected void Open()
{
Connection.Open();
}
protected void Close()
{
Connection.Close();
}
protected int ExecuteNonQuery()
{
return Command.ExecuteNonQuery();
}
protected object ExecuteScalar()
{
return Command.ExecuteScalar();
}
protected DbDataReader ExecuteReader()
{
return Command.ExecuteReader();
}
}
E poi lo sto implementando due volte perché so che il mio progetto verrà eseguito almeno su due server: MSSql Server e MySQL.
public class MsSqlServerDAL : AbstractDAL
{
public MsSqlServerDAL(string ConnectionString)
{
Command = new SqlCommand();
Connection = new SqlConnection(ConnectionString);
Command.Connection = Connection;
}
}
// Here will be MySqlServerDAL
Ora potrò creare la classe pubblica FootballProjectDAL
che ha un membro privato di% tipoAbstractDAL
, e posso inserire in FootballProjectDAL
tramite il costruttore la variazione implementata di AbstractDAL
.
Spero che abbia senso.
public class FootballProjectDAL : IFootballProject
{
private AbstractDAL dal;
public FootballProjectDAL(AbstractDAL DAL)
{
dal = DAL;
}
//
// Interface IFootballProject Implementation.
//
public List<Competition> GetCompetitionByName(string Name)
public List<Competition> GetCompetitions()
public List<Competition> GetCompetitionsByCountry(Country Country)
public List<Country> GetCountries()
public List<Country> GetCountriesByParent(string Parent)
public Country GetCountryByName(string Name)
public List<Group> GetGroups()
public List<Phase> GetPhases()
public Season GetSeasonByName(string Name)
public List<Season> GetSeasons()
public List<Team> GetTeamByName(string Name)
public List<Team> GetTeams()
public List<Team> GetTeamsByCountry(Country Country)
// Here will be all the other methods from IFootballProject interface.
}
Per finalizzare ho aggiornato la foto iniziale.
Ancora grazie mille per il tuo tempo e i tuoi consigli.
Leggi altre domande sui tag design