Tja! Någon här måste väll veta om detta är rätt vis att göra? kanske man inte alls ska ha en klass i varje skickt som binder ihop alla klasser innom just det skicktet? Nja, jag är nog inte helt förtjust i din ide eftersom jag anser att BL-klassen bör vara ett interface som ärver åtminstone från IDataReader och sannolikt ett par andra interface med. Sedan så skapar man unika BL-klasser utifrån den eller möjligtvis en abstrakt basklass som hämtar in ett DAL som man i sin tur skapar sina BL-klasser ifrån. Jag brukar ha en pool att hämta DAL ifrån så jag brukar inte ha den som singleton heller, detta för att då blir BL och DAL mycket enkla att anpassa mot andra datakällor och flytta runt till andra projekt, desutom tål applikationen flertrådning mycket bättre. aha, IDataReader? Kan du kanske bifoga ett exempel på hur du menar? Igentligen är inte singelton ett krav, det var bara ett exprement från min sida, jag funderade på threadsafe och hur singelton objekt hanteras i Webapplicationer. Har varje användare ett eget singelton objekt så hade det troligen gått bra, men delar alla på samma singelton objekt så är det giviet vis stor risk för att det inte blir threadsafe. Glöm IDataReader, det var fel... Använd IListSource så kan du koppla dina BL som DataSources in i dina webkontroller. Lite onödigt att implementera en egen pool, Intressant lösning, men frågan är om man verkligen behöver en pool? Det är ju meningen att varje användare ska ha ett Objekt av BLL och ett BLL ska ha ett objekt av DAL, sen ska detta användas till alla operationer som användare önskar göra. Jag anser att man behöver en pool för att inte lasta ner databasen alltför hårt, objekt från poolen använder man ju enbart när man gör faktiska databasanrop (insert, update & select) övrig tid använder man inte datakopplingarna alls. Jag förstår hur du menar, men det finns helt klart saker att tänka igenom här. <b>1. Det blir en del extra logik för att hantera köer och poolen vilket så klart kommer dra lite prestanda från webservern dessutom kan man fundera på hur detta blir underhålls mässigt.</b> bra svar, detta kan ju faktiskt trotts allt vara något att titta på. <b>Du nämner något om att man genomför en lastbalansering för att se vad var och en får i response tid? Hur gör man detta?</b> aha låter bra! <b>Men jag förstår inte var du menar att kösystemet ska ligga?</b> Jepp jag förstår, jag ska absolut prova detta, tack för de bra tippsen =D Återkommer med resultat. Hmm det är en grej jag inte gillar, och det är att ha SQL kod i BLL, jag kommer troligen lägga detta i DALet efter som det känns fel att bygga upp ett beronde mellan BLL och Databasen(ex syntax för mySQL och msSQL). SQL-koden ska enligt min åsikt ligga i databasen och i BLL, i databsen skapar du stored procedures och i BLL anropar du dessa, vill du sedan byta databasmotor så flyttar du bara med dina stored procedures till den nya databasen. Detta stoppar iofs ett användande av Access men å andra sidan är Access rätt otrevligt om man testar FireBird, syntaxskillnaderna sköter du dessutom inom dina stored procedures... Jag håller med om att man ska använda StoredProcedures så långt det går, men i vissa fall kan det behövas dynamiskt genererad SQL commandon. Att lägga till ett lager för SQL är en ganska god ide i många fall, däremot är det aldrig en god ide att generera SQL på BLL-sidan, mitt råd är dock att så långt det går ska du hålla SQL'n i databasen och anropen för SQL'n i BLL, behöver du generera SQL som är specialanpassad i något läge kan du göra det på följande vis: Låter som en bra idé, men problemet kvarstår dock med paramertar: Hmm, jag förstår inte riktigt vad problemet är om du modellerar dina BLL så här: Tackar så mycket för den givande informationen! <b>Jag har börjat med att bygga en klass som ska hålla i utdelning av DataAccessObject. Tanken är att klassen ska vara statisk så att alla jobbar mot samma projekt.</b> Låter bra! <b>Om jag förstår dig rätt så ska jag ställa det anropade objectet i kö är det ex en klass som heter tools som gör anropet så ska detta objekt sättas in i kön?</b> För att tydliggöra litegranna om hur jag har gjort mitt affärslager och dal så slängde jag ihop en kortfattat och icke komplett kod som du kan bygga ditt ifrån, detta är dock avsett för klientapplikationer som arbetar ensamma mot en lokal databas så ingen pool eller kö finns med i bilden men det borde vara lätt att placera in sådana funktioner när du fattat min röriga kod: Helt klart intressant lösning, den ser dock inte alls ut som den jag har jobbat fram. Jag ska ta en närmare titt på din kod så snart jag har tid(sitter på jobbet nu). Visst kan jag kika på din lösning men jag hoppas du ser det eleganta i min där jag använder Generics och har möjligheten att stoppa in all validering och liknande i en centralt placerad abstrakt klass. Att sedan mitt DAL är tämligen simpelt har egentligen inte med saken att göra då basklasserna är ganska anpassningsbara utan att förändra gränssnittet alltför mycket. Den största nackdelen är att koden är ganska uselt anpassad för databaser utan naturliga nycklar men det är inget som hindrar mig nämnvärt... oj, många svar och mkt att läsa.. har inte läst allt men tänkte bara flika in med lite punkter: Tja Fredrik! >>Vissa säger att SQL kommandona ska genereras/skapas i BLL vilket gör att BLL måste känna till typen av databas(MySQL/MSSQL), BLL har inte en direkt kontakt med databasen utan istället så känner den endast till DALet, i dalet finns metoder så som ExecuteScaler, ExecuteNonQuery o.s.v. BLL använder dessa för att exikvera sina frågor. >>vill bygga om applikationen så att den passar en annan bransch(Business) Själklart måste man i de flesta fall ändra datamodelen men om vi då kör en variant där SQL kommandona ligger i BLL så är det igentligen bara BLL och själva databasmodel(ej DAL) som du behöver anpassa till den nya bransch(Business). Hej Jimmy, <b>Om ditt businesslager skapar sql kod/objekt/whatever så har du ju dataaccess från ditt bll. >>Så du ville mena på att DAL egentligen ska vara en businesslayer-factory kombinerat med en persistmetod för varje businesslayer? <b>Nu gjorde du en strawman av det hela.Singelton eller inte?
Jag jobbar på en 3 skikts lösning (Precentation Logic/Business logical layer/DataAccess layer). Jag har funderat hur kommunikationen ska se ut mellan dessa skikt.
Min idé är att alla websidor i PL ärver från en basklass. Denna basklass kommer att skapa upp ett objekt (singelton) av BLL på följande vis
PL
protected BusinessLogicalLayer.BusinessLogicalLayer businessHandler = BusinessLogicalLayer.BusinessLogicalLayer.GetBusinessLogicalLayerInstance();
Som vi kan se så har vi alltså en klass i BLL som heter BusinessLogicalLayer som her en static metod GetBusinessLogicalLayerInstance :
klass BusinessLogicalLayer i BLL
public class BusinessLogicalLayer
{
private static BusinessLogicalLayer instance;
private static SeviceLibrary serviceLibrary;
private static DataAccessLayerHandler dataAccessLayerHandler;
public static BusinessLogicalLayer GetBusinessLogicalLayerInstance()
{
if (instance == null)
instance = new BusinessLogicalLayer();
return instance;
}
public SeviceLibrary SeviceLibrary
{
get
{
if (serviceLibrary == null)
serviceLibrary = SeviceLibrary.GetInstance();
return serviceLibrary;
}
}
public DataAccessLayerHandler DataAccessLayerHandler
{
get
{
if (dataAccessLayerHandler == null)
dataAccessLayerHandler = DataAccessLayerHandler.GetInstance();
return dataAccessLayerHandler;
}
}
}
Som vi kan se så ser klassen till att den är ett singelton objekt. Det skapas det även upp instanser av andra objekt i BusinessLogicalLayer klassen, och många av dessa objekt är också singelton objekt. Vi kan även se att en instans av DALet skapas här på liknande vis som BLL skapades i basklassen i PL(D.v.s singelton).
För att man ska få tillgång till funktionaliteten som finns så benämns dessa som static. Detta gör att man lätt kommer åt dem från PL i Design time.
DAL:et ser ut på liknande vis som BLL:
DataAccessLayer DALL
public class DataAccessLayer
{
#region Members
private static DataAccessLayer instance = new DataAccessLayer();
private DataBaseHandler dbHandler = new DataBaseHandler();
private ExceptionLogger exceptionLogger;
#endregion
#region Contructor
public DataAccessLayer()
{
dbHandler = new DataBaseHandler();
this.exceptionLogger = new ExceptionLogger(dbHandler);
}
public static DataAccessLayer GeInstance()
{
if (instance == null)
instance = new DataAccessLayer();
return instance;
}
#endregion
#region Properties
public ExceptionLogger ExceptionLogger
{
get {
if (exceptionLogger == null)
exceptionLogger = new ExceptionLogger(dbHandler);
return exceptionLogger;
}
}
Tittar vi närmare på ex ExceptionLogger som är en klass som loggar fel direkt till databasen så behöver denna en databas förbindelse, denna upprättas i DataAccessLayer.dbHandler vilket gör att DataAccessLayer måste skicka med en referens till denna klass till ExceptionLogger som ska göra anrop till databasen.
Jag har några frågor på denna lösning:
1. Anledningen till att jag vill ha ett objekt i BLL och DAL som håller samman hela biblioteket är för att ex inte det ska skapas upp flera objekt av samma typ(ex DataBaseHandler)
2. Jag använder mig av singelton objekt så långt det går för att spara resurser, men frågan är om det kan innebära andra problem?
3. När det gäller singelton objekt i denna lösning kommer varje användare som går in på siten få sitt eget singelton objekt att jobba mot eller kommer alla besökare jobba mot samma objekt?
Kanske det är bättre att strunta i att ha någo huvud klass som håller samman och istället benämner all funktionalitet som public static så att man kommer åt den på ett enkelt vis i lagret ovan för?
Sv: Singelton eller inte?
Sv: Singelton eller inte?
Vill du hindra att flera objekt av samma typ skapas så kan du skapa dina BL-komponenter som singleton om du vill, men du bör fundera igenom vad som kommer att ske om flera användare vill använda och redigera datat i affärslagret samtidigt, då anser jag det vara bättre att göra någon form av awarness som gör att redigeringen är avstängd för nästa användare som hämtar ut ett objekt tills det att den första har bearbetat klart det han har.Sv:Singelton eller inte?
Sv: Singelton eller inte?
Så här någonting skulle jag vilja påstå att man kan bygga sitt system, du får fylla i tomrummen själv men iden är att varje affärsklass implmenteras av en lista och en grundklass, DAL hämtas enbart via poolen och själva datahämtningen sker generellt i din BL-basklass, det blir lite extrakodande men till slut har man ett bibliotek som är skalbart och återanvändbart:
<code>
public class DataAccessPool
{
private static m_poolcounter = 0;
private const int poolmax = 5; // Antal dataaccesslayers man kan ha uthämtade som max
private const int poolttl = 3; // Time to live för varje access-lager (kodar inte in den hanteringen, den får du sköta själv)
private List<DAL> m_dataaccesslayers = new List<DAL>();
public static DAL GetDAL()
{
if (m_poolcounter == poolmax)
return null; // Poolen är tom
m_poolcounter++;
m_dataaccesslayers.Add(DAL.GetDAL());
return m_dataaccesslayers[m_poolcounter - 1];
}
public static void ReturnDAL(DAL dataaccesslayer)
{
m_dataaccesslayers.Delete(dataaccesslayer); // Du fattar iden va?
m_poolcounter--;
}
}
public class DAL // Ditt dataaccesslager, inte som en singleton men den ska inte gå att skapa utanför poolen
{
private DAL();
protected static DAL GetDAL() {return new DAL();}
}
public abstract class BL
{
// Implementera datahämtning
}
public class Personer : BL, IListSource
{
private List<Person> m_personer = new List<Person>();
}
public class Person
{
private string m_namn;
public Namn {get{return m_namn} set {
m_namn = value;
// Kod för att hålla reda på huruvida det här fältet är uppdaterat eller inte här
}}
}
</code>Sv:Singelton eller inte?
om man behöver detta så varför inte använda COM+ (EnterpriseServices)?
exempel
[ Transaction(TransactionOption.RequiresNew) ]
[ ObjectPooling(true, 5, 10) ]
public class EmployeeMaintenance : ServicedComponentSv:Singelton eller inte?
Det var alltså inte tanken att varje användare ska möjlighet att använda 5 instanser av BLL samtidigt, och inte ihäller att BLL ska kunna använda 5 instanser av DAL.
Jag ser att du använer dig av static på poolingen innebär detta inte att endast 5 samtidiga objekt av den givna typen kan vara utkäckade åt gången? vad händer om vi har 100 samtidiga användare? det känns dumnt att sätta en sådan gräns? men jag kan ha förstått ditt exempel fel?Sv: Singelton eller inte?
<b>Det är ju meningen att varje användare ska ha ett Objekt av BLL och ett BLL ska ha ett objekt av DAL, sen ska detta användas till alla operationer som användare önskar göra.</b>
Nej det är inte meningen, meningen är att alla användare som vill ska ha ett BLL medan varje BLL lånar ett objekt från poolen när användaren gör en operation med just sitt BLL.
<b> vad händer om vi har 100 samtidiga användare? </b>
Då får varje användare vänta i kön (en kö måste givetvis implementeras) för att få låna ett DAL-objekt från poolen, på så vis så får du aldrig flera användare som jobbar mot databasen samtidigt än det antal objekt du tillåter i poolen. Fördelen är att du kan anpassa poolens storlek mot databasens och serverns kapacitet utan att användarna blir lidande för att hundra stycken försöker skriva i databasen samtidigt. Har du ett väldigt transaktionsrikt system så blir vinsterna enorma eftersom ingen kommer att få vänta på sitt data när de väl fått låna ett objekt från poolen, skriver du sen ett smart kösystem så kan du låta vissa användare gå före i kön så de användarna aldrig behöver vänta längre än det tar för den snabbaste operationen som ligger i kö.
Ola funderade på varför jag inte använder COM+, och det gör jag inte eftersom jag vill ha full kontroll på min pool och dess kö samt även kunna ha den portabel över de plattformar som stödjer .Net. Prestandamässigt kanske det inte är den bästa modellen men det är bra mycket snabbare än att låta alla användare hålla en anslutning till databasen var.
För övrigt ska poolen givetvis ha en metod för att lämna tillbaka ett DAL och metoden där du hämtar ut ett DAL ska givetvis ha en inbyggd köfunktion.Sv:Singelton eller inte?
Följande problem ser jag:
1. Det blir en del extra logik för att hantera köer och poolen vilket så klart kommer dra lite prestanda från webservern dessutom kan man fundera på hur detta blir underhålls mässigt.
2. Att säga att det är max 100 st som samtidigt får jobba mot databasen känns fel, det kan ju vara så att SQL servern faktiskt klarar av 500 samtidiga anslutningar utan några problem alls, då har jag igentligen begränsat min site trotts att beräknings kraften finns där. Hur vet jag hur många samtidiga anslutningar databasen klarar?
3. Om man nu vill ha någon form av connection pool, finns inte detta inbyggt i databasen? alltså att man kan säga åt den att endast ta emot si och så många connections?
Jag ser ärligt talat inte vinsten i lösningen, om det är så att vi når ett tak där SQL databasen får jobba väldigt mycket så finns det ju ändå inget annat alternativ än att upgradera servern eller optimera koden. Att låta det gå snabbt för vissa och långsamt för andra känns inte särskilt intressant lösning.
Självklart kan man tänka sig att instanser till databasen skapas endast när de behövs, men att hålla på att skapa objekt för minimala anrop tar tid det med.Sv: Singelton eller inte?
Rent kodmässigt är en kö inte så besvärlig att implementera och själva poängen med ett kö och poolsystem är att spara in på prestandaåtgången på servern. Är koden klar och fungerar så kommer den inte att kräva något underhålla att tala om.
<b>2. Att säga att det är max 100 st som samtidigt får jobba mot databasen känns fel, det kan ju vara så att SQL servern faktiskt klarar av 500 samtidiga anslutningar utan några problem alls, då har jag igentligen begränsat min site trotts att beräknings kraften finns där. Hur vet jag hur många samtidiga anslutningar databasen klarar?</b>
Du säger gvetvis inte att det är max 100 som får jobba mot databasen, du anpassar antalet DAL i poolen efter vad sql-servern klarar av, hur många den klarar av avgör du enkelt med ett last-test där du fingerar ett antal klienter som gör de tyngsta operationerna i databasen (både skriv och läs) samtidigt och minskar antalet klienter tills alla får en rimlig responstid, då har du fått ett ungefärligt antal DAL du kan ha poolen samtidigt, normalt så klarar webservern mångdubbelt flera request än vad sql-servern klarar av vilket inte är något problem eftersom varje webrequest betyder inte nödväntigtvis ett anrop i databasen. På jobbet har vi en webserver med ungefär 15000 (ungefär 1000 samtidiga) användare på den klarar sig med tre DAL i poolen utan att någon klagar.
<b>3. Om man nu vill ha någon form av connection pool, finns inte detta inbyggt i databasen? alltså att man kan säga åt den att endast ta emot si och så många connections?</b>
Jo det finns men du har inte samma kontroll över den och kan inte prioritera anropen mellan användarna.
<b>Jag ser ärligt talat inte vinsten i lösningen, om det är så att vi når ett tak där SQL databasen får jobba väldigt mycket så finns det ju ändå inget annat alternativ än att upgradera servern eller optimera koden. Att låta det gå snabbt för vissa och långsamt för andra känns inte särskilt intressant lösning.</b>
Med en pool-lösning är koden redan optimerad på förhand, normalt så kan du köra med ett 10 till 20 samidiga anslutningar till databasen, betänk att varje anslutning inte bör ta mer än ett par millisekunder till ett par tiondelar i anspråk, det innebär att 1000 samtidiga anrop gör att den som är sist i kön max får vänta 5-10 sekunder på att få utföra sitt anrop, för att få till 1000 samtidiga anrop måste du antagligen ha minst tjugotusen samtidiga användare, fundera på vilket som äter mest prestanda, 20000 samtidiga öppna anslutningar till databasen varav 1000 skriver samtidigt eller 20 öppna anslutningar som skriver sekventiellt.
<b>Självklart kan man tänka sig att instanser till databasen skapas endast när de behövs, men att hålla på att skapa objekt för minimala anrop tar tid det med.</b>
Det är också därför man har en pool, där ligger objekten redo för att utföra en databasfunktion de skapas bara en gång och de där bara om de råkar ut för en timeout, annars så ligger de latent där hela tiden så att användarna kan låna in dem vid behov.Sv:Singelton eller inte?
Du nämner något om att man genomför en lastbalansering för att se vad var och en får i response tid? Hur gör man detta? Det finns något i Visual Studio där man kan spela in ett flöde och sen sätta hur många användare som ska köra igenom slingan(här kan man även sätta in så att viss veration i tid görs) är det detta du menar? kan man se response tiden för var och en eller ett medeltal?
Själva kö systemet bör alltså byggas i BLL om jag förstått det rätt?Sv: Singelton eller inte?
http://support.microsoft.com/kb/231282
<b>Själva kö systemet bör alltså byggas i BLL om jag förstått det rätt?</b>
Varje gång en användare vill skicka in sitt data till databasen så anropar han en metod i BLL som i sin tur ställer sig i kö till databaspoolen, så kön är alltså ett objekt mellan alla BLL och själva poolen. Den faktiska implementationen skulle jag vilja påstå att den ska ligga i poolen. Flödet skulle kunna se ut så här:
BLL har en metod som heter WriteData(), den anropas när användaren trycker på Spara-knappen, den anropar Poolens metod RequestDALForWriteData(BLL BLLToWrite) är poolen tom så placerar den objektet BLLToWrite i kön, när ett DAL blivit ledigt så signalerar poolen detta till kön som i sin tur avgör vilket BLL som ska få det lediga DAL'et och anropar Poolens metod för att leverera DAL'et till BLL, leveransen sker via ett event och sedan så släpper BLL DAL'et genom att anropa poolens metod ReleaseDAL(DAL DALToRelease).Sv:Singelton eller inte?
Men jag förstår inte var du menar att kösystemet ska ligga? ska det vara ett eget classlibrary mellan BLL och DALL eller bör det ligga i BLL?
Jag har en klass i DAL:et som jag kallar DataBaseHandler, denna innehåller ex ExecuteScaler o.s.v, denna används av alla andra klasser i DALet som ska göra anrop till databasen. Man skulle kunna tänka sig att man bygger in kö systemet i DAL:et och att man då bygger runt denna DataBaseHandler? Frågan är om detta kanske är en bra lösning?
Slutligen så gör jag antagandet att den modul/klass som sköter kösystemet bör bara static så att alla jobbar mot samma.
Tackar så mycket för länken, ska se om det är samma saker som ingår i den Visual Studio versionen jag kör.Sv: Singelton eller inte?
Jag menar så här:
BLL är en basklass som kan läsa och skriva till databasen genom att hämta ett DAL från poolen.
Alla dina specifika BLL ärver från BLL-basklassen och är ansvariga för att skapa den sql som krävs för att datat ska kunna skrivas ner i databasen.
Poolen är en singletonklass som äger en Kö-klass som också är singleton, när ett objekt av typen BLL efterfrågar ett DAL från poolen så väljer poolen om ett DAL ska returneras eller om förfrågan ska placeras i kö-klassen.
Kö-klassen håller reda på alla förfrågningar som väntar på att få ett DAL och prioriteringen för varje förfrågan, när ett DAL blir ledigt i poolen så signalerar poolen till kön att nu är ett DAL ledigt och kö-klassen tilldelar nästa BLL i kön detta DAL. Närr BLL'n är klar med sitt tilldelade DAL så lämnar BLL'n tillbaka DAL'et till poolen som i sin tur signalerar till kön att detta DAL blivit ledigt.
DAL är en klass som arbetar mot databasen och implementerar t.ex. ExecuteScalar och antagligen ExecuteReader samt ett par funktioner till. Den ska inte vara av typen singleton eftersom poolen är det enda stället som får skapa och lämna ut DAL-objekt, poolen får inte heller förstöra dessa objekt eftersom en av poängerna med poolen är att man enbart ska skapa en begränsad uppsättning DAL som i sin tur återanvänds så ofta som möjligt.
Ingen av klasserna eller metoderna har behov av att ha några static-funktioner (sånär som på singleton-objekten).
Tydligare än så här har jag svårt att bli men jag hoppas du förstår iden och syftet. Iden är i korthet att många användare samtidigt inte ska belasta databasen mer än den pallar med (poolen innehåller funktionalitet för detta), webservern ska på ett snabbt sätt kunna tilldela databasanslutningar baserat på olika prioriteringar man kan tänkas göra (kö-systemet implementerar detta). Syftet är i korthet att få en lösning som med enkla handgrepp kan anpassas efter den miljön man kör på, kör man på en klen webserver med en slö databas så låter man poolen bara innehålla ett eller två DAL medan man i en kraftfullare miljö kanske kan låta poolen innehålla 100 DAL.Sv:Singelton eller inte?
Sv:Singelton eller inte?
Frågan är om detta är en smak sak och så klart beronde på applikationen man bygger?Sv: Singelton eller inte?
Sv:Singelton eller inte?
Ialla fall så skiljer sig även exeveringen av StoredProcedures från databas till databas. ex MySQL används exec SP medan i MS SQL använder man call SP.
Dessutom så använder jag paramertarar på command objektet när jag skapar upp en fråga till databasen, d.v.s när jag skapat MySQLCommand/SQLCommand så tilldelar jag en sträng likannde exec mySP/call mySP. Sen lägger jag till defenierade parametrar till detta command objekt, detta så att man fångar fel där man försöker tilldela värden som inte är av rätt typ/längd. Skulle man skötta detta uppe i BLL så måste jag skicka ner command objekt och det blir det MySQLCommand eller SQLCommand beronde på databas. Visst jag kan skicka ner parametrar till DALet men då måste det vara särskilda funktioner ex getUserEmail(int UserId).
Jag förstår fördelen med att ha med SQL i BLL då man lättare kan byta ut BLL till ett annat, ex från en site som behandlar brädspel till en site som behandlar bilar. Men samtidigt, ska man byta databas så måste man ändra i BLL, DAL och DATABAS. Detta är kanske inte något man gör så ofta så man får fundera över vad tanken är med lösningen.
Kanske man skulle lägga ett lager till mellan BLL och DAL som innehåller SQLen?Sv: Singelton eller inte?
Skapa en basklass eller ett interface som heter: IBLLHelper och som innehåller en metod som heter GenerateSQL. I de fall du behöver generera SQL för t.ex. BLL'n för Person mot databasen MySQL så skapar du helt enkelt följande class:
class PersonHelperMySQL : IBLLHelper
{
public string GenerateSQL(<parametrar>)
{
<generera MySQL-sql här och returnera den>
}
}
I Person så gör du följande:
string GetSQL()
{
IBLLHelper myhelper;
if (supporteddatabase == SupportedDatabases.MySQL)
myhelper = new PersonHelperMySQL();
else if (supporteddatabase == SupportedDatabases.MSSQL)
myhelper = new PersonHelperMSSQL();
string result = myhelper.GenerateSQL(<parametrar>);
myhelper.Dispose();
return result;
}
På så vis blir du iofs bunden till specifika databaser i BLL men du kan göra så mycket som möjligt av allt jobb där det bör utföras och du är inte direkt bunden till en specifik databas. Dessutom kan du på det här sättet expandera helperklassen med fler specialanpassningar utan att gå in och påverka gränssnittet i ditt BLL.Sv:Singelton eller inte?
private static void UpdateDemographics(Int32 customerID)
{
string commandText = "CALL mySP(@ID);";
using (SqlConnection connection = new SqlConnection(this.connectionString))
{
SqlCommand command = new SqlCommand(commandText, connection);
command.Parameters.Add("@ID", SqlDbType.Int).Value = customerID;
try
{
connection.Open();
Int32 rowsAffected = command.ExecuteNonQuery();
Console.WriteLine("RowsAffected: {0}", rowsAffected);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Detta gör jag för att unvika SQLInjections, och som man kan se så specificerar man vad det är för parametrar som ska in, det kan ex vara VarChar(36) för en sträng som är max 36 tecken lång.
Så frågan är då ska man skicka SQLCommand objekt till DALet? detta betyder såfall att BLL måste istort sätt kunnam er om databas operationer än vad DALet behöver(DALet håller bara coonection och erbjuder några få metoder som executeScaler).
Sv: Singelton eller inte?
class Person : IBLL
{
public string name {get.. set..}
public int id {get.. set..}
// från IBLL, returnerar true om allt gick bra
public bool Persist()
{
if (Injected(name)) // En funktion du kan ha static som validerar för sql-injections
return false;
if (Injected(id)) // En funktion du kan ha static som validerar för sql-injections
return false;
if (updated)
sql = "CALL sp_updateperson(@ID, @NAME);";
else if (created)
sql = "CALL sp_createperson(@NAME);";
// bygg ditt sqlcommand och kör det här
return true;
}
}
<b>det kan ex vara VarChar(36) för en sträng som är max 36 tecken lång.</b>
Den checken har du i set-propertien i din BLL
<b>Så frågan är då ska man skicka SQLCommand objekt till DALet?</b>
Det är en variant, en annan är att skicka en sql-sträng en tredje är att låta DALET bygga SQL-kommandot och BLL förser det med en parameterlista i form av en List eller Array eller liknande. Personligen så skulle jag skicka sql-strängar som var färdigkomponerade och låta min BLL sköta allt tänkande tillsammans med ett gäng helpers som kunde avgöra om värdet är okej att skicka ner i det formatet det är.Sv:Singelton eller inte?
Jag vill gärna ventilera mina lösningar under tiden jag utvecklar för att få en respons som kan peka på om jag går ett rätt eller fel håll, jag tycker redan jag har fått mycket information här.
Jag har börjat med att bygga en klass som ska hålla i utdelning av DataAccessObject. Tanken är att klassen ska vara statisk så att alla jobbar mot samma projekt.
DataAccessObjecten kommer kapslas in i en klass(DataAccessPoolObject) som håller reda på om DataAccessObjectet används dessutom så ser det också till så att det är rätt tråd som jobbar mot DataAccessObjectet. Detta betyder att när DataAccessPoolObject blir aktiverat(tilldelad till en tråd) så sparas trådens unika id ner i objektet, detta id kontrolleras varje gång som man försöker komma åt DataAccessObjectet i DataAccessPoolObject.
Detta gör att trådar som av någon anledning inte lämnat tillbaka DataAccessPoolObject till poolen inte kan komma åt DataAccessObjectet.
DataAccessPoolObject har även en timer inbyggd som är satt på 5 min som standard, men man kan även vid initieringen sätta den till godtycklig tid. Denna timer gör att när tiden gått ut så stängs anslutningen (connection.close()) och DataAccessPoolObject anses vara inaktivt(det går inte att komma åt DataAccessObjectet).
För att notifiera poolen om när DataAccessPoolObject skiftar läge(aktivt/inaktivt) så används en event, när eventen kastas i den statiska klassen så görs en uppdatering på poolen.
När en tråd kommer in till den statiska klassen som håller DataAccess poolen så läggs denna direkt in i en List<Thread>, efter detta sätts den i en while loop som kommer rulla så länge tråden inte fått access till ett DataAccessPoolObject. I denna loop används Thread.Sleep(200), när tråden väl vaknar så går den in i en lock(this) sektion (för att få thread safe) där tråden då kontrollerar om den ligger på position 0 i List<Thread> listan, om den gör detta så ska den loopa igenom DataAccessPoolen för att se om det finns något objekt som är inaktivt och isåfall ta detta för bruk(och registrera sitt tråd id) . Om inte det finns ett objekt ledigt så kommer den återgå till sleep(200) för att göra ett nytt försök.
Det kan vara en bra ide att använda sig av det event som DataAccessPoolObjectet kastar för att väcka den tråd som är nästa på tur, jag är dock inte säker på hur man kan göra detta?
Är jag på rätt spår här?Sv: Singelton eller inte?
Gör den som en singleton istället, då håller servern ett objekt latent i minnet som dina klienter bara behöver peka på, har du det statiskt så måste servern enumerera varenda anrop, static bör du bara använda till metoder med ytterst specifika syften.
<b>Det kan vara en bra ide att använda sig av det event som DataAccessPoolObjectet kastar för att väcka den tråd som är nästa på tur, jag är dock inte säker på hur man kan göra detta?</b>
Använd en metod, det frågande objektet ställer sig i kö via ett metodanrop, när det blivit tilldelat ett DAL så plockar ut det ur kön och kräver att det anropar en "lämna tillbakametod" som genast tilldelar det lediga dalet till nästa i kön.
Annars känns det som om du är på rätt spår...Sv:Singelton eller inte?
<b>Använd en metod, det frågande objektet ställer sig i kö via ett metodanrop, när det blivit tilldelat ett DAL så plockar ut det ur kön och kräver att det anropar en "lämna tillbakametod" som genast tilldelar det lediga dalet till nästa i kön.</b>
Om jag förstår dig rätt så ska jag ställa det anropade objectet i kö är det ex en klass som heter tools som gör anropet så ska detta objekt sättas in i kön? Hur fungerar då detta? säg att jag lägger in objetet tools(som är en hjälpklass) i en list<object>, tråden som kom in med anropet kommer stå och sova tills det finns ett ledigt DataAccessPoolObjectet då binder man upp DataAccessPoolObjectet med tools objectet förstället tråden vilket gör att alla operationer som tools ska genomför har tillgång till en DataAccessPoolObjectet(så läng tiden inte gått ut).
Som jag ser det så är det myket enklare att sätta själva tråden i kö och då binda DataAccessPoolObjectet till denna tråd, det kan så klart betyda att tråden utför flera anrop mot DataAccessPoolObject(även om det är från olika objekt(tools, authentication)).Sv: Singelton eller inte?
Visst kan du som du själv säger sätta själva tråden i kö men som du själv också kommit fram till så får ett problem med flera anrop mot databaspoolen, att sätta objektet i kö har nackdelen att det är betydligt krångligare men fördelen av att det blir färre anrop totalt sett. En annan fördel med att hålla objekten i kö är att du kan serialisera dom och sedan bara deserialisera de gränssnitt du behöver.
Vidare kan man tänka sig en tredje modell med dynamiska factories där man koverterar det köande objektet till ett köobjekt med färdiga körningar som när det är klart skapar ett nytt returobjekt av rätt typ i en factory-klass när det är klart, på så vis undviker du att hålla reda på något mer än följande:
1. Vad ska utföras
2. Vem ska ha tillbaka ett resultat
3. Av vilken typ ska resultatet vara
Hur du gör är upp till dig, jag skulle anpassa lösningen efter mina kunskaper och det tidsutrymme som fanns tillgängligt i första hand, sedan skulle jag fundera på vilken last systemet kan tänkas utsättas för.Sv: Singelton eller inte?
// Interfacet för mitt DAL
public interface IDataAccessLayer
{
object ExecuteScalar(IDbCommand command);
IDataReader ExecuteReader(IDbCommand command);
int ExecuteNonQuery(IDbCommand command);
string ConnectionString { get; set; }
void Open();
void Close();
IDbCommand CreateCommand();
}
// Interfacet för affärslagret
public interface IBusinessLayer
{
System.Data.DataRowState State { get; set; }
void Delete();
void Persist();
}
// Den abstrakta klassen för affärslagret
public abstract class BusinessLayer : IBusinessLayer
{
private DataRowState m_state = DataRowState.Unchanged;
private IDataAccessLayer m_dataaccesslayer = null;
public IDataAccessLayer DataAccessLayer { get { return m_dataaccesslayer; } set { m_dataaccesslayer = value; } }
public DataRowState State { get { return m_state; } set { m_state = value; } }
public void Delete() { State = DataRowState.Deleted; }
public void Persist()
{
IDbCommand l_command = null;
if (m_state == DataRowState.Added)
l_command = AddedCommand();
else if (m_state == DataRowState.Deleted)
l_command = DeletedCommand();
else if (m_state == DataRowState.Modified)
l_command = ModifiedCommand();
}
protected abstract IDbCommand AddedCommand();
protected abstract IDbCommand DeletedCommand();
protected abstract IDbCommand ModifiedCommand();
protected bool Updated(string old_value, string new_value)
{
if (m_state == DataRowState.Added || m_state == DataRowState.Deleted)
return false;
if (old_value != new_value)
return true;
return false;
}
protected bool Updated(int old_value, int new_value)
{
if (m_state == DataRowState.Added || m_state == DataRowState.Deleted)
return false;
if (old_value != new_value)
return true;
return false;
}
protected bool Updated(ulong old_value, ulong new_value)
{
if (m_state == DataRowState.Added || m_state == DataRowState.Deleted)
return false;
if (old_value != new_value)
return true;
return false;
}
protected bool Updated(double old_value, double new_value)
{
if (m_state == DataRowState.Added || m_state == DataRowState.Deleted)
return false;
if (old_value != new_value)
return true;
return false;
}
}
// En abstrakt klass för att bygga collections
public abstract class BusinessLayerCollection<T>
{
private List<T> m_items = new List<T>();
private IDataAccessLayer m_dataaccesslayer = null;
public IDataAccessLayer DataAccessLayer { get { return m_dataaccesslayer; } set { m_dataaccesslayer = value; } }
public List<T> Items { get { return m_items; } }
public void Fetch()
{
IDbCommand l_command = FetchCommand();
m_dataaccesslayer.Open();
IDataReader l_reader = m_dataaccesslayer.ExecuteReader(l_command);
object[] l_datarow = new object[l_reader.FieldCount];
while (l_reader.Read())
{
l_reader.GetValues(l_datarow);
m_items.Add(FillWidth(l_datarow));
}
if (m_items.Count > 0)
m_items.Sort(GetComparer());
l_reader.Dispose();
m_dataaccesslayer.Close();
l_command.Dispose();
}
public void Persist()
{
foreach (IBusinessLayer l_item in m_items)
l_item.Persist();
}
public void Add(T item)
{
m_items.Sort((IComparer<T>)item);
int l_index = m_items.BinarySearch(item, (IComparer<T>)item);
if (l_index < 0)
m_items.Add(item);
else
FillWidth(m_items[l_index], item);
}
protected abstract IDbCommand FetchCommand();
public abstract void FillWidth(T destination, T source);
public abstract T FillWidth(object[] datarow);
protected abstract IComparer<T> GetComparer();
}
// Ett exempeldal för MS-SQL
public class MSSQL_DataAccessLayer : IDataAccessLayer
{
private SqlConnection m_connection = new SqlConnection();
#region Singleton
private static MSSQL_DataAccessLayer m_dal = new MSSQL_DataAccessLayer();
public static IDataAccessLayer GetDal()
{
return (IDataAccessLayer)m_dal;
}
private MSSQL_DataAccessLayer() { }
#endregion
#region IDataAccessLayer Members
public object ExecuteScalar(IDbCommand command)
{
SqlCommand l_command = (SqlCommand)command;
l_command.Connection = m_connection;
return l_command.ExecuteScalar();
}
public IDataReader ExecuteReader(IDbCommand command)
{
SqlCommand l_command = (SqlCommand)command;
l_command.Connection = m_connection;
return (IDataReader)l_command.ExecuteReader();
}
public int ExecuteNonQuery(IDbCommand command)
{
SqlCommand l_command = (SqlCommand)command;
l_command.Connection = m_connection;
return l_command.ExecuteNonQuery();
}
public string ConnectionString { get { return m_connection.ConnectionString; } set { m_connection.ConnectionString = value; } }
public void Open()
{
m_connection.Open();
}
public void Close()
{
m_connection.Close();
}
public IDbCommand CreateCommand()
{
return (IDbCommand)new SqlCommand();
}
#endregion
}
// Ett exempel för hur man kan göra en player-klass, denna saknar en del funktionalitet där man skapar command-objekten
class Player : BusinessLayer, IComparer<Player>
{
private int m_identity = 0,
m_netidentity;
public int Identity
{
get { return m_identity; }
set
{
if (Updated(m_identity, value))
m_identity = value;
}
}
public int NetIdentity
{
get { return m_netidentity; }
set
{
if (Updated(m_netidentity, value))
m_netidentity = value;
}
}
private string m_name = "";
public string Name
{
get { return m_name; }
set
{
if (Updated(m_name, value))
m_name = value;
}
}
private IDbCommand m_insertcommand = null,
m_deletecommand = null,
m_updatecommand = null;
protected override System.Data.IDbCommand AddedCommand()
{
if (m_insertcommand == null)
{
m_insertcommand = DataAccessLayer.CreateCommand();
m_insertcommand.CommandText = "insert into plr_players plr_name, net_identity values (?, ?);";
}
}
protected override System.Data.IDbCommand DeletedCommand()
{
if (m_deletecommand == null)
m_deletecommand = DataAccessLayer.CreateCommand();
}
protected override System.Data.IDbCommand ModifiedCommand()
{
if (m_updatecommand == null)
m_updatecommand = DataAccessLayer.CreateCommand();
}
#region IComparer<Player> Members
public int Compare(Player x, Player y)
{
return String.Compare(x.Name, y.Name);
}
#endregion
}
// Och så här gör man players-klassen, denna funkar dock förutom att Persist() inte är fullt implementerad i Player
class Players : BusinessLayerCollection<Player>
{
private IDbCommand m_command = null;
private IComparer<Player> m_comparer = null;
protected override System.Data.IDbCommand FetchCommand()
{
if (m_command == null)
{
m_command = DataAccessLayer.CreateCommand();
m_command.CommandText = "select * from plr_players;";
}
return m_command;
}
public override void FillWidth(Player destination, Player source)
{
destination.Name = source.Name;
destination.Identity = source.Identity;
destination.NetIdentity = source.Identity;
}
public override Player FillWidth(object[] datarow)
{
Player l_player = new Player();
l_player.Identity = (int)datarow[0];
l_player.Name = (string)datarow[1];
l_player.NetIdentity = (int)datarow[2];
return l_player;
}
protected override IComparer<Player> GetComparer()
{
if (m_comparer == null)
m_comparer = (IComparer<Player>)new Player();
return m_comparer;
}
}
// Och så här kan det se ut i riktigt kod:
Players p = new Players();
p.DataAccessLayer = MSSQL_DataAccessLayer.GetDal();
p.DataAccessLayer.ConnectionString = @"ANSLUTNING TILL DATABASEN";
p.Fetch();
foreach (Player pl in p.Items)
Console.WriteLine(pl.Name);
Hoppas du får ut något av denna kodmassa...
Sv:Singelton eller inte?
Jag har kommit så långt så att det finns en grund som man nu igentligen skulle kunna bygga vad som hällst för system på. Jag funderar på om jag ska packa ihop detta och lägga ut så att ex du kan ta en titt på det, jag tror att koden är ganska lätt att följa, dessutom så har jag kommenterat den en hel del.
Kanske även du kan få något ut av det, om du har tid vill säga?Sv: Singelton eller inte?
Sv: Singelton eller inte?
BLL ska inte veta något om databasen, inte ens IDataReder interfacet etc. BLL ska bara hantera affärslogik och få data från DAL. Självklart måste BLL veta vad för data returnerar, men inte vart ifrån datan kommer.
DAL har som uppgift att gå mot databakällan och ska inte veta om DAL.
Undvik Singelton om du kan, dom behövs inte, använd Spring.Net istället ;)
Se till att så sent som möjligt öppna din connection, och stäng den så tidigt som möjligt, det gör att connection poolen används mer effektivt.
Jag brukar iofs inte köra med PL/BLL/DAL seperation som man gjorde ofta under COM+ tiden, utan använder DDD (Domain driven design) idag.
När jag har tidigare gjort mitt data access lager så kör jag enbart med statiska metoder. Jag brukar implementer eller använda mig av en provider baserat ramverk som initierar mina data access providers (klasser som går mot en specifik datakälla). Den ser till att lägga undan instancen i en static variabel, men det är mest för att det sker en late-binding vid initiering av mina providers. Om jag inte kör med late utan early binding, så skulle jag inte bry mig om att lägga dom i en static eller cache etc. Utan initiera dom vid behov.
Här är lite kod som inte kör med providers, men för att visa en enkel lösning som jag skulle använda om jag inte använder mig av DDD:
BLL (Efter DNA):
public static Customer GetCustomer(int customerID)
{
//validera input..
Customer customer = CustomerDA.GetCustomer(customerID)
//utför logik
return customer;
}
DAL:
public static Customer GetCustomer(int customerID)
{
SqlCommand command = DBHelper.PrepareCommand("GetCustomer");
command.Paramters.AddWithValue("cutomerID", customerID);
command.Connection.Open();
SqlDataReader reader = command.ExecuteReader(....);
if (reader.HasRows())
{
reader.Read();
Customer customer = new Customer();
//fyll customer
reader.Close();
}
command.Connection.Close();
return customer;
}
Om jag inte har affärslogik som ska utföras så hoppar jag över BLL lagret och går med en gång mot DAL ifrån mitt presentation lager. OBS! Jag jobbar inte på detta viset nu, utan efter DDD och använder OR-Mappers etc..
Jag brukar ofta använda mig av controllers i mitt web site projekt som hantera flöden, tillstånd etc, och anropar modell.
/Fredrik Normén
ASP.Net MVP - Cornerstone
blog: http://fredrik.nsquared2.comSv:Singelton eller inte?
Det ser ut att finnas två såkallade skolor angånde hur seperationen mellan DAL och BLL ska se ut. Vissa säger att SQL kommandona ska genereras/skapas i BLL vilket gör att BLL måste känna till typen av databas(MySQL/MSSQL), detta gör å andra sidan att DALet int kommer ha metoder som är BLL bundna som ex GetCustomer.
Vissa menar att endast DALet ska känna till databasen och därför ska SQL kommandona genereras/skapas i DALet och därmed publisera metoder så som GetCustomer. Detta gör att DALet blir på sätt och vis specialliserat för BLL, om man väljer att byta ut BLL till en helt annan typ av branch så måste man också ändra DALet.
Vilken typ man ska välja är helt beronde på hur tanken bakom sin applikation ser ut. Kan man tänka sig att BLL kommer vara fast(grunden kommer inte användas för andra siter) så kan det helt klart vara mer intressant att kunna byta databas på ett lättare vis utan att röra runt i BLL(där det finns massa annan kod som inte berör databasen). Är det så att man bygger en grund för flera siter och vet att man antagligen kommer använda en specefik databas hela tiden(vilket oftast är fallet) så tycker jag nog att man kan lägga in SQL generering/skapandet i BLL då man ändå måste byta ut detta när man bytar branch för siten.Sv: Singelton eller inte?
Om ditt businesslager skapar sql kod/objekt/whatever så har du ju dataaccess från ditt bll.
DAL står ju för data access layer... och det är precis vad du gjort från ditt bll.
Om du har båda DAL och BLL klasser som båda skapar/använder databasrelaterade objekt så har du mislyckats med dina lager , du har smetat ut ansvaret i två lager.
varför försöka bygga flerlagerslösningar om man iaf ska smeta ansvar mellan lagren?
Helgalet IMO..Sv:Singelton eller inte?
Detta betyder då att BLL kommer innehålla SQL kommandon som är bunda till en viss databas(ex MSSQL eller MYSQL) efter som syntaxen skiljer sig något i SQL kommandona, just därför blir det svårare att byta databas men samtidigt blir det lättare om man vill bygga om applikationen så att den passar en annan bransch(Business) efter som det är endast BLL(och möjligen PL) som behöver byggas om.Sv: Singelton eller inte?
hur ska du bygga om den så den passar en annan business utan att ändra den data du jobbar mot?
möjligen att du kan ändra lite beräkningar o regler etc, men det är ju knappast något som påverkar sqlsatserna..
Om ditt bll innehåller sqlsatser så är det ju delvis ett dal.
ditt bll känner till hur din databasmodell ser ut.
ändrar du datamodellen kommer ditt bll haverera pga att du har massa trasiga sqlsatser där.Sv:Singelton eller inte?
Lägger du SQLen i DAL så måste du byta ut BLL(efter som du valt annat bransch), DALet(Då det innehåller BLL specefika metod namn så som GetSpecificUserGrades) dessutom måste man så klart ändra datamodelen.
Vi kan alltså genom att lägga SQLen i BLL slippa att göra något med DALet så länge vi använder samma typ av databas för den nya branschen.
Jag har läst lite om detta men inte fått helt klarhet i vad som gäller och jag har diskuterat denna fråga i annan tråd men det ser som sagt ut att finnas två "skolor" och jag ser helt klart fördel/nackdel i de båda.
Man kan så klart ta saken till ytterlighet och lägga ett servic lager mellan BL och DAL som gör just SQL genereringen, detta betyder att detta sevicelager kommer ligga väldigt när BL och måste bytas när BL bytas.Sv: Singelton eller inte?
Jag har nog missat den delen där man genererar SQLen i BLL, visst kan det finnas behov men då skulle jag använda mig av query-object och ändå låta DAL sköta skapandet av själva SQL frånga, detta för att separera affärslogiken från att veta vilken datakällan den ska accessa. Tanken med seperation är att centralicera affärslogiken, separera den från DAL för att kunna återanvändas oavsätt datakälla. Du ska tex lätt kunna byta ut dina DAL att gå mot XML tex istället för en databas. Om du då genererar SQL i BLL, så måste din DAL som går mot XML, kunna tolka SQLen och göra om den tex till en XPATH etc. Jag har gått ifrån denna design och bygger mina lösningar med hjälp av DDD.
/Fredrik Normén
ASP.Net MVP - Cornerstone
blog: http://fredrik.nsquared2.comSv:Singelton eller inte?
DAL står ju för data access layer... och det är precis vad du gjort från ditt bll.</b>
Så du ville mena på att DAL egentligen ska vara en businesslayer-factory kombinerat med en persistmetod för varje businesslayer?
Jag är inte helt nöjd med att hålla med om de tankarna men det hänger ihop med varje inviduell lösning. Jag ser DAL som ett simpelt lager som enbart sköter kommunikationen med databasen medan varje businesslayer ska ta sitt ansvar vad gäller kännedom om databasen. Visst kan man generalisera det mera och göra en SQL-objekt-factory vars enda uppgift är att känna till datamodellen och leverera SQL-objekt till affärslagrerna, eller ännu mer genom att ha ett objekt emmellan som kopplar ihop affärlager med SQL-objekt. Detta finner jag dock vara alltför generellt och alltför knöligt för den som inte absolut måste kunna vara helt databsoberoende i varenda litet fall.Sv: Singelton eller inte?
>>Jag är inte helt nöjd med att hålla med om de tankarna
Nu gjorde du en strawman av det hela.
jag sa inte det där..
Jag sa att bll inte ska innehålla databas relaterad kod, tex sqlsträngar,eller ado objekt
har man det så är ditt bll ett dal.
I NPersist så är tex våra entiteter helt frånkopplade från allt vad persistens heter.
helt rena klasser som bara innehåller data och ev businesslogik för entiteten.
all kommunikation med db sker utanför, frikopplat från entiteterna.Sv:Singelton eller inte?
jag sa inte det där..</b>
Nej det gjorde du inte men det var så jag tolkade det du sa.
<b>Jag sa att bll inte ska innehålla databas relaterad kod, tex sqlsträngar,eller ado objekt
har man det så är ditt bll ett dal.</b>
För mig hänger det ihop med vad ett DAL innebär, för mig innebär ett DAL just inget mer än ett accesslager mot databasen, affärslagret är för mig inte bara regelverket kring hur man beskriver datat och formulerar reglerna kring hur det får användas utan också hur det ska lagras, hämtas och uppdateras, dvs sql koden som omger datat. Att jag ser det på det sättet kan hänga ihop vilken typ av applikationer jag utvecklar / utvecklat eftersom jag håller med om att din syn på det verkar vettigare i samband med websystem med mer dynamiska databasberoenden.
Kärnpunkten för mig i den här tråden är i varje fall det faktum att oavsett hur ditt affärslager ser ut så är det viktigare med själva DAL'et och hur man accessar det i en fleranvändarmiljö än vad affärslagret har för faktiska uppgifter. I de flesta fall kan nämligen ett affärslager som innehåller sql portas mellan olika databaser utan någon som helst handpåläggning då databaserna normalt innehåller möjligheten att köra stored procedures alternativt stödjer ansi-sql.