webForumDet fria alternativet

SQL i affärslager?

19 svar · 1 494 visningar · startad av echoSwe

echoSweMedlem sedan nov. 20041 189 inlägg
#1

Hej!

En liten fråga - ditt DAL-lager ska ju vara databasspecifikt och tanken är att abstraktionen ska vara tillräckligt stor för att du ska kunna byta DAL utan att byta affärslager. Hur gör du då för att utföra komplexa-SQL-frågor som även använder funktioner?

  1. Lägger du det som stored procedures? (stödjs ju av de flesta produktionsdatabaser)

  2. Skriver du din komplexa syntax i ditt DAL som separata funktioner och låter sedan ditt DAL returnera tex. XML, DataSets, DataTable, Structs osv...?

Sedan, om du har byggt ett community - säg att du vill skicka ett mail till alla som vill bli påminda om att en vän fyller år - var lägger du då SQL-en för denna fråga? I ditt DAL eller i ditt affärslager?

Eller har jag fått fel på det här?

Sedan så funkar det väl så att när man vill använda sina lager, så instansierar man dem genom att referera till dll-filen och sedan skriva:
NameSpace.MittDal objDAL = new MittDal();

och låter sedan detta lager returnera tex. en datatable för varje fråga?

Sen undrar jag vad som kännetecknar en "collection" - är det helt enkelt en tabell med till exempel redan behandlad data (tex. om en blogg - antal kommentarer, senast lagda kommentar, posten, författare) som är hämtad från de olika tabellerna i databasen?

Kan man på något sätt få med relationerna om man använder ett dataset - alltså utan att sätta dem programmatiskt?

Sista frågan då - om du har en shoppingapplikation, i beställningsfasen... Du väljer att hämta ett (typat?) dataset med alla lagda beställningar. Sedan lägger du till din nya beställning genom affärslagret genom att först lägga till raden i ditt dataset och sedan köra update med en dataadapter - vad händer då om du har fått in en beställning sedan datan hämtades till din dataset? Låter man då MS SQL gå med på att fortsätta och lägga in den nya datan, trots att tabellerna inte är identiska?

GladhMedlem sedan maj 20012 812 inlägg
#2

En liten fråga - ditt DAL-lager ska ju vara databasspecifikt och tanken är att abstraktionen ska vara tillräckligt stor för att du ska kunna byta DAL utan att byta affärslager. Hur gör du då för att utföra komplexa-SQL-frågor som även använder funktioner?

  1. Lägger du det som stored procedures? (stödjs ju av de flesta produktionsdatabaser)

  2. Skriver du din komplexa syntax i ditt DAL som separata funktioner och låter sedan ditt DAL returnera tex. XML, DataSets, DataTable, Structs osv...?

Mitt DAL tar emot en IDBCommand objekt och returnerar ett DataTable. Det betyder att i mitt affärslager så bygger jag upp detta IDBCommand som jag skall använda mig av, och det kan antingen vara för en SP eller för en Parameteriserad SQL fråga och får tillbaka ett DataTable. Sedan skall affärslagret mappa om denna DataTable till de objekt som jag arbetar på.

Eftersom jag är en lat människa så vill jag inte hålla på med denna mappning hela tiden och har istället en O/R Mapper som skapar mina SQL frågor (de flesta) och skickar IDBCommand objektet till mitt DAL och sedan omvandlar DataTablen till objekt.

Sedan, om du har byggt ett community - säg att du vill skicka ett mail till alla som vill bli påminda om att en vän fyller år - var lägger du då SQL-en för denna fråga? I ditt DAL eller i ditt affärslager?

Helt klart i ditt affärslager, ditt DAL är ointresserad vad din SQLfråga skall göra, den skall bara exekvera den och returnera resultatet inte bygga upp den, eftersom denna funktion är specifik för din applikation.

Sedan så funkar det väl så att när man vill använda sina lager, så instansierar man dem genom att referera till dll-filen och sedan skriva:
NameSpace.MittDal objDAL = new MittDal();

och låter sedan detta lager returnera tex. en datatable för varje fråga?

Japp!

Sen undrar jag vad som kännetecknar en "collection" - är det helt enkelt en tabell med till exempel redan behandlad data (tex. om en blogg - antal kommentarer, senast lagda kommentar, posten, författare) som är hämtad från de olika tabellerna i databasen?

Collection är en samling av data, så en DataTable är en collection av en massa DataRows, men oftas när man pratar om collections så menar man oftas att den är en samling av "egna objekt". Alltså du har ett objekt som heter Customer, så har man en CustomerCollection som innehåller en samling av Customer.

Kan man på något sätt få med relationerna om man använder ett dataset - alltså utan att sätta dem programmatiskt?

Nu vet jag inte så mycket om dataSet eftersom jag är av den uppfattning att det är en styggelse, men jag har svårt att se hur du skulle få med den information från din databas i en vanlig SQL fråga. Så jag är nästan helt övertygad att du måste sätta dem programmatiskt efter/innan du hämtar din data.

Du väljer att hämta ett (typat?) dataset med alla lagda beställningar. Sedan lägger du till din nya beställning genom affärslagret genom att först lägga till raden i ditt dataset och sedan köra update med en dataadapter - vad händer då om du har fått in en beställning sedan datan hämtades till din dataset?

Har lite svårt att förstå vad du menar, men menar du att du först hämtar alla gjorda beställningar till ett DataSet och visar dessa för kunden, kunden lägger sedan till de nya beställningar till DataSetet vilket nu kommer att innehålla dels redan gjorda beställningar och de nya beställningar som inte är gjorda än. Och sedan så gör du en Update ner till databasen. Om det är så du menar så verkar det vara ett otroligt omständingt sätt att lägga till nya beställningar till databasen. Varför iall världen vill du blanda in redan gjorda beställningar. Du skall bara skicka ner den nya beställningarn till databasen, vill kunden se sina gammla beställningar så visas de på en Historik sida som är ReadOnly eftersom man inte skall ha möjlighet att ändra i någon redan gjord beställning. Det löser även ditt problem...

- M

echoSweMedlem sedan nov. 20041 189 inlägg
#3

Hej Gladh och tack för svaret. Nu äntligen är min extended essay inlämnad och jag har fått lite tid till att fiffla en aning med programmering.

Då har jag tittat lite på hur man kan göra och i brist på bättre sidor så har jag kollat på https://www.asp.net. Har även läst en del på MSDN "Passing data through tiers"...

Vad säger ni om den här koden?
DataAccessLayer:

using System;
using System.Data;
using System.Data.Odbc;
using System.Configuration;
using ByteFX.Data.MySqlClient;

/// <summary>
/// The DAL for MySQL 5.0
/// </summary>
public class BlogDB
{
	public BlogDB() {}

    public static DataSet GetEntry(int id, DataSet ds)
    {
        System.Data.IDbConnection objConn = MySqlConnection(ConfigurationManager.ConnectionStrings["MySQL"].ConnectionString);
        
        //simple selection string. Make sure the DAL works before moving onto something more advanced.
        string query = "SELECT blog_entries.entryId, blog_entries.title, blog_entries.date, "+
           "blog_entries.authorId, blog_entries.entry, blog_entries.commenting, blog_entries.catId "+
           "FROM blog_entries WHERE entryId = '@entryId' ORDER BY blog_entries.date ASC";
        System.Data.IDbCommand objCmd = new MySqlCommand(query, objConn);

        System.Data.IDataParameter objParam = MySqlParameter();
        objParam.ParameterName = "@entryId";
        objParam.DbType = MySqlDbType.Int;
        objParam.Value = Convert.ToString(id);
        objCmd.Parameters.Add(objParam);

        System.Data.IDbDataAdapter objDa = new MySqlDataAdapter();
        objDa.SelectCommand = objCmd;
        objDa.Fill(ds);
        return ds;
    }
    public static DataSet GetEntryByAuthors(string _authorId)
    {
 
    }

    public DataSet GetSqlEntry(string sql, DataSet ds)
    {
        //same as above but with a custom SQL.
        return ds;
    }

    //the other three - update, delete, insert
    public int DeleteEntry(BlogEntry _entry)
    {
        // ...WHERE entryId '@entryId'
        // ....Value = _entry.Id;
    }
    public int DeleteEntry(string _entryId)
    {
        System.Data.IDbConnection objConn = MySqlConnection(ConfigurationManager.ConnectionStrings["MySQL"].ConnectionString);
        System.Data.IDbCommand objCmd = MySqlCommand("DELETE * FROM blog_entries WHERE entryId = '@entryId';", objConn);

        System.Data.IDataParameter objParam_Del = new MySqlParameter();
        objParam_Del.ParameterName = "@entryId";
        objParam_Del.Value = _entryId.ToString();
        objCmd.Parameters.Add(objParam_Del);

        objConn.Open();
        return objCmd.ExecuteNonQuery(); //returns rows affected
        objConn.Close();
    }
}

Business Layer:

using System;
using System.Data;
using System.Collections.Generic;

/// <summary>
/// blog entries business layer
/// </summary>
public class BlogEntryComponent
{
	public BlogEntryComponent()
	{
		//
		// TODO: Add constructor logic here
		//
	}
    public List<BlogEntry> GetEntriesByAuthor(string _author, string _sortExpression)
    {
        List<BlogEntry> = new List<BlogEntry>();
        DataSet ds = new DataSet();

        //not implemented yet.
    }

    public List<BlogEntry> GetEntriesByMonth(string monthNumber)
    {
        List<BlogEntry> = new List<BlogEntry>();
        // yep
    }

    public List<BlogEntry> GetEntries()
    { 
        //get all the entries in the database, i.e. lots of work further down.
    }

    //osv
}

BlogEntry.cs:

using System;

/// <summary>
/// Instance of a blog entry
/// </summary>
public class BlogEntry
{
    private string id;
    public string Id
    {
        get { return id; }
        set { id = value; }
    }

    private string author;
    public string Author
    {
        get { return author; }
        set { author = value; }
    }

    private string nbrComments;
    public string NbrComments
    {
        get { return nbrComments; }
        set { nbrComments = value; }
    }

    private string entry;
    public string Entry
    {
        get { return entry; }
        set { entry = value; }            
    }

    private string date;
    public string Date
    {
        get { return date; }
        set { date = value; }
    }

    private string category;
    public string Category
    {
        get { return category; }
        set { category = value; }
    }
	public BlogEntry(string _id, string _author,
        string _nbrComments, string _entry, string _date, string _category)
	{
        this.Id = _id;
        this.Author = _author;
        this.NbrComments = _nbrComments;
        this.Entry = _entry;
        this.Date = _date;
        this.Category = _category;
	}
    public BlogEntry()
    {
        //default constructor
    }
}

Är jag på rätt spår? Är något fel i koden? Jag använder mig av ByteFX gränssnitt till MySQL

Är det här som menas med collections? På vilket sätt använder jag Generics i den här koden? Är det smart att skicka med ett dataset i funktionsanropet ner till DAL när jag vill hämta en BlogEntry eller ska jag skapa ett DataSet, eller vad ni nu vill, i DAL istället och skicka den uppåt?

echoSweMedlem sedan nov. 20041 189 inlägg
#4

Kom igen någon - koden är lättläst och jag vill gärna ha svar :)

cyprysMedlem sedan dec. 20003 563 inlägg
#5

Ok. Jag har nästan ingen industriell erfarenhet av hur de större projekten är uppbyggda vad gäller DAL, men mina egenbyggen skiljer sig en del från dina.

T.ex. Har du gjort projektspecifika (deletentry etc) i ditt DAL, vilket jag tycker är fel. Det DAL jag använder, kopierar jag över till mina olika projekt eftersom dess enda uppgift är att exekvera och returnera, inte alltid returnera heller utom exceptions.

Min uppbyggnad är m.h.a. de interface som finns är i stil med.

-- DAL
pulbic createCommand, 
getDataTable(sqlQuery), 
getScalar(sqlQuery), 
getDataSet(sqlQuery),
runNonQuery(sqlQuery)

-- BL
Klassen blog
private id
public deleteEntry() {
  Dal oDal = new Dal()
  iDbCommand oCom = oDal.createCommand()
  oCom.commandText = "delete from tbl where id = " & id
  oDal.runNonQuery(oCom) 
}
osv
echoSweMedlem sedan nov. 20041 189 inlägg
#6

Hej

Det var ju lite smartare kanske - du använder alltså ett interface - IDbCommand och i ditt DAL så har du den implementerad så att när du anropar metoden "createCommand" så skapas en databas-specifik "Command" så att säga?

Och din runNonQuery både öppnar och stänger uppkopplingen då...

Har jag fått rätt på generics/collections i alla fall då?

Återkommer om koden ;). Tack för hjälpen!

Min konstiga metodologi kom från
http://www.asp.net/QuickStart/aspnet/doc/data/objects.aspx#dal

echoSweMedlem sedan nov. 20041 189 inlägg
#7

Så ditt CreateCommand ser ut såhär typ?

    public static IDbCommand CreateCommand()
    {
        try
        {
            IDbCommand objCmd = new MySqlCommand();
        }
        catch (Exception e)
        {
            throw e;
        }
        return objCmd;
    }

...och vad är en scalar?

cyprysMedlem sedan dec. 20003 563 inlägg
#8

Mitt createCommand ser ut som

public class DataAccessLayer
{
    public class DataLayer  {

    static protected IDbConnection defaultDataConnection = 
    new System.Data.SqlClient.SqlConnection(
    ConfigurationManager.ConnectionStrings["connStrLocal"].ConnectionString);
    
    public IDbCommand createCommand() {
            return defaultDataConnection.CreateCommand();
        }
    }
}

En scalar är en metod för att returnera en sträng av värdet högst upp till vänster i en databastabell man får genom att anropa en db med en sql-select-sats. command.executeScalar()

Och din runNonQuery både öppnar och stänger uppkopplingen då...

Yupp.

echoSweMedlem sedan nov. 20041 189 inlägg
#9

Om man tittar på din kod:

public deleteEntry() {
  Dal oDal = new Dal()
  iDbCommand oCom = oDal.createCommand()
  oCom.commandText = "delete from tbl where id = " & id
  oDal.runNonQuery(oCom) 
}

Om du skriver att "runNonQuery(sqlQuery)" - varför skickar du då med ett command?
Säg att jag från affärslagret vill hämta ett inlägg, men att presentationslagret skickar id-et som en parameter in i min metod i affärslagret - då vill jag lägga till parametrar i min command - men hur gör jag det om jag använder IDataParameter?

Såhär?

    public static DataSet GetEntry(int id, DataSet ds)
    {
        DataAccessLayer oDal = new DataAccessLayer();
        IDbCommand objCmd = oDal.CreateCommand();
        objCmd.CommandText = "SELECT blog_entries.entryId, blog_entries.title, blog_entries.date, " +
           "blog_entries.entry, blog_entries.commenting " +
           "FROM blog_entries WHERE entryId = '@entryId' ORDER BY blog_entries.date ASC";
        System.Data.IDataParameter oParam;
        oParam.ParameterName = "@entryId";
        oParam.Value = id;
        objCmd.Parameters.Add(oParam);
        ds = oDal.GetDataSet(/*????*/);
        return ds;
}

eller har du överladdade/overloaded metoder i ditt dal som kan ta in IDataCommand som parametrar?

Om jag arbetar direkt mot databasen kan jag ju göra såhär:

System.Data.IDataParameter objParam_Del = new MySqlParameter();

men eftersom du då säger att man bara ska ha några få funktioner i data accesslagret så förstår jag inte hur det då ska funka?
Skriver du din SQL som jag har gjort ovan och formulerar en ny fråga för varje metod i ditt BL?
Hur använder du namespaces?
Vad används en scalar till då? Varför vill man ha värdet högst upp till vänster i en databastabell?
När du kör din createCommand() i ditt DAL - skapar datorn då en direktreferens till detta objektet? Hur funkar det tillsammans med tex. MySqlCommand() som finns i klassbiblioteket som jag importerar?

Om du skriver

  oCom.commandText = "delete from tbl where id = " & id

öppnar du inte upp för sql injedctions i så fall - något du slipper om du använder parametrar istället för att bara lägga ihop strängar?

Har du bara fyra metoder i ditt dataaccesslager?
Varför kan man inte instansiera såhär?

class dal {
private static MySqlCommand command;
public IDbCommand Command {
set {
this.command = value;
}
}
public dal(IDbCommand oCmd) { 
this.Command = oCmd;
}
}

- jag vill alltså kunna lägga till parametrar utan att göra det i DAL - går det? Hur gör ni det?

Har du läst nån bok om det här, eller hur lärde du dig?

cyprysMedlem sedan dec. 20003 563 inlägg
#10

Om du skriver att "runNonQuery(sqlQuery)" - varför skickar du då med ett command?

Därför att det är ett command jag vill exekvera. Antingen en ren sql-sträng eller SP. Jag har överlagring som tar emot både strängar och commands.

Säg att jag från affärslagret vill hämta ett inlägg, men att presentationslagret skickar id-et som en parameter in i min metod i affärslagret - då vill jag lägga till parametrar i min command - men hur gör jag det om jag använder IDataParameter?

IDbCommand objCmd = oDal.CreateCommand();
IdataParameter oParam1 = objCmd.createParameter();

Ungefär som du skrivit i ditt inlägg.

eller har du överladdade/overloaded metoder i ditt dal som kan ta in IDataCommand som parametrar?

Nej. Behöver jag skicka parametrar skapar jag ett command och lägger till dem i BL och skickar bara oCommand.

men eftersom du då säger att man bara ska ha några få funktioner i data accesslagret så förstår jag inte hur det då ska funka?

Se ovan hur jag skapar parametrar

Skriver du din SQL som jag har gjort ovan och formulerar en ny fråga för varje metod i ditt BL?

Någonting åt det hållet ja.

Hur använder du namespaces?

Med fingertoppskänsla (??).

Vad används en scalar till då? Varför vill man ha värdet högst upp till vänster i en databastabell?

Ex
select count(*) as antal from myUsers

ExecuteScalar returnerar i det här fallet strängen "42" om det finns 42 användare.
Ett 'recordset' skulle returnera en row innehållandes columnen antal 42.

När du kör din createCommand() i ditt DAL - skapar datorn då en direktreferens till detta objektet?

Jag skapar då ett command som fungerar tillsammans med den connection som används för tillfället.
Ny sorts connection = ny sorts commandObjekt.

Hur funkar det tillsammans med tex. MySqlCommand() som finns i klassbiblioteket som jag importerar?

Eftersom jag arbetar gentemot interfaces behöver man inte bry sig. Hela grejen med att använda den här metoden.

öppnar du inte upp för sql injedctions i så fall - något du slipper om du använder parametrar istället för att bara lägga ihop strängar?

Det beror väl på hur mycket jag kontrollerat att id är valid indata?

Har du bara fyra metoder i ditt dataaccesslager?

Istort sett. createCommand, runNonQuery samt funktioner för att hämta data på olika sätt.
Samtliga exekverar på samma sätt m.h.a. privata metoder.

jag vill alltså kunna lägga till parametrar utan att göra det i DAL - går det? Hur gör ni det?

Jag har en funktion i DAL som tar en parameter som är skapad av oCom.createParameter() i BL samt dess typ och värden och adderar den till oCom.
I stil med

cmd.Parameters.Add(
  oData.fillParameter(
    cmd.CreateParameter, "@user", DbType.Int64, userId
  )
)

Har du läst nån bok om det här, eller hur lärde du dig?

Jag ställde en fråga här och Gladh svarade. Sen är google och diverse manualer och exempel min väg att vandra.
Men jag tycker inte jag 'kan' det här.

echoSweMedlem sedan nov. 20041 189 inlägg
#11

Tänka sig - jag lyckades få det att funka ;)
http://194.236.30.84/hafse/ListEntries.aspx
(i mkt tidigt pre-alpha stadium ;))

Återkommer med fler frågor i och med att jag bygger till saker på lagren ;)

m_soderlundMedlem sedan sep. 20026 425 inlägg
#12

Det blir runtime error!

Var kan jag läsa mer om detta ämne, programmeringslogik och uppbyggnad av lager i applikationer? Det mesta är självförklarande men om man vill fördjupa sig mer? Tack.

echoSweMedlem sedan nov. 20041 189 inlägg
#13

Så, nu e det fixat ;)

echoSweMedlem sedan nov. 20041 189 inlägg
#14

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/distapp.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcustomhttpmodules.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/apparchch2.asp
Dessa sidor är ganska jobbiga att ta sig igenom, och den som jag fick mest användning av vad "passing data through tiers", men det var inte förrän jag hittade:
http://www.asp.net/QuickStart/aspnet/doc/data/objects.aspx#dal
som jag tog tag i det och tänkte att "nu har jag tillräcklig kunskap för att bygga nåt i den här stilen", trots att deras exempel är något felkonstruerat.

http://www.webforum.nu/showthread.php?s=&postid=829907#post829907
http://pdc.se/blog/DisplayEntry.aspx?eid=5

Är det som jag har läst hittills. Sedan har jag beställt en bok om design patterns in c# via Malmö Stadsbibliotek. Jag tar gärna emot lite tips om andra bra sidor.

I min aspx sida så använder jag följande kod - som gör att du kan referera direkt till din collection via asp:ObjectDataSource och sedan länka direkt till dess id från din GridView eller vad du nu använder genom att ange: DataSourceID="ObjectDataSource1"
som attribut till din GridView.

Hela koden:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ListEntries.aspx.cs" Inherits="ListEntries" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
		<asp:GridView ID="GridView1" Runat="server" DataSourceID="ObjectDataSource1" AutoGenerateColumns="False"
			AllowPaging="True" AllowSorting="True" CellPadding="4" DataKeyNames="Id" EmptyDataText="-" ForeColor="#333333" GridLines="None">
			<Columns>
				<asp:BoundField DataField="Category" HeaderText="Category" SortExpression="Category" />
				<asp:BoundField DataField="Entry" HeaderText="Entry" SortExpression="Entry" />
				<asp:BoundField DataField="Id" HeaderText="Id" SortExpression="Id" />
				<asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
				<asp:BoundField DataField="NbrComments" HeaderText="NbrComments" SortExpression="NbrComments" />
				<asp:BoundField DataField="Author" HeaderText="Author" SortExpression="Author" />
				<asp:BoundField DataField="Date" HeaderText="Date" SortExpression="Date" />
			</Columns>
			<FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
			<RowStyle BackColor="#EFF3FB" />
			<EditRowStyle BackColor="#2461BF" />
			<SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
			<PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
			<HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
			<AlternatingRowStyle BackColor="White" />
		</asp:GridView>
		
		<asp:ObjectDataSource ID="ObjectDataSource1" Runat="server" TypeName="BlogEntryComponent"
			SelectMethod="GetAllEntries" SortParameterName="_sortExpression">
			<SelectParameters>
				<asp:ControlParameter ControlID="GridView1" DefaultValue="date" Name="_sortExpression"
					PropertyName="SelectedValue" Type="String" />
			</SelectParameters>
		</asp:ObjectDataSource>
		<br />
		<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
	</div>
    </form>
</body>
</html>

men här använder jag en massa saker som bara finns i ASP.Net 2.0.

echoSweMedlem sedan nov. 20041 189 inlägg
#15

Har nu fixat till det med en <asp:Repeater ... /> också.
Bygger vidare på hela bloggen från och med nu på
http://194.236.30.84/hafse

Någon som har en aning om varför den bara hämtar tre inlägg?

		<asp:Repeater ID="rptEntries" runat="server" DataSourceID="objEntriesDS">
			<HeaderTemplate><h2>Blog entries</h2></HeaderTemplate>
			<ItemTemplate>
				<h3><a href="#" title="Click here to read it"><%# DataBinder.Eval(Container.DataItem, "Title") %></a></h3>
				<p style="margin-top: 0;font-size: 70%; font-family:arial;">Published <%# DataBinder.Eval(Container.DataItem, "Date") %> by <%# DataBinder.Eval(Container.DataItem, "Author") %> in <%# DataBinder.Eval(Container.DataItem, "Category")%></p>
				<%# DataBinder.Eval(Container.DataItem, "Entry") %>
			</ItemTemplate>
			<AlternatingItemTemplate><div class="hr" style="height: 1px; border-bottom: 1px solid #aaa;">&nbsp;</div></AlternatingItemTemplate>
		</asp:Repeater>
		&nbsp;</div>
		<asp:ObjectDataSource ID="objEntriesDS" runat="server" SelectMethod="GetAllEntries"
			TypeName="BlogEntryComponent">
			<SelectParameters>
				<asp:Parameter DefaultValue="date" Name="_sortExpression" Type="String" />
			</SelectParameters>
		</asp:ObjectDataSource>
emissionMedlem sedan dec. 19996 721 inlägg
#16

Att bygga DAL på det sätt som ni beskriver är definitivt användbart, men abstraktionsnivån blir så låg att man nästan inte kan tala om ett DAL. Om affärslagret måste ha kunskap om de verktyg (via t.ex. IDbCommand) som används för att hämta data så blir det i princip en del av DAL:et. Ett lager som returnerar och jobbar med IDbCommand et. al. blir snarare ett PAL, Provider Access Layer (ja, jag hittade på den termen just nu...:)). Användbart och återanvändbart, men något abstraherande DAL är det inte.

Den kod du visade i inlägg tre, echoSwe, är däremot ett DAL + BL och övrig kod du visat här indikerar att du definitivt fått bra grepp om det hela. Ska man vara petig så finns det ingen anledning att använda sig av t.ex. IDbConnection, eftersom du ändå kapslat in det hela som en MySQL-specifik klass.

echoSweMedlem sedan nov. 20041 189 inlägg
#17

Godnatt Emission,

Intressant term ;) har inte hört den förut. Så jag ligger och balanserar på något sätt mellan ett DAL och ett BL?

Ett DAL - tanken är att man ska kunna använda ett till alla projekt - är inte interfaces (IDbCommand etc) vägen som man får gå då? Ett affärslager har självklart hand om mer avancerade saker också, som att kolla transaktioner, kommunicera med externa tjänster/webservices och bygga upp collections till de presentationslager som behöver dem. Är det inte typ det som jag har gjort här?

BlogEntryComponent.cs (extract)

using System;
using System.Data;
using System.Collections.Generic;

/// <summary>
/// blog entries business layer
/// </summary>
public class BlogEntryComponent
{
	public BlogEntryComponent()
	{
		//
		// TODO: Add constructor logic here
		//
	}

    public List<BlogEntry> GetAllEntries(string _sortExpression)
    {
        List<BlogEntry> entries = new List<BlogEntry>();
        DataAccessLayer oDal = new DataAccessLayer();
        DataTable oDt = oDal.GetDataTable(
            "SELECT blog_entries.title, blog_entries.entry, blog_entries.date, blog_entries.entryId, blog_entries.authorId, " +
            "  blog_authors.name AS author," +
            "  COUNT(blog_comments.CommentID) AS nbrComments " +
            "FROM blog_entries " +
            "INNER JOIN blog_authors ON blog_entries.authorId = blog_authors.authorId " +
            "/* here it would be great to add some checkup on what categories this entry is in and concenate a string with them*/ " +
            "LEFT OUTER JOIN blog_comments ON blog_entries.entryId = blog_comments.entryId " +
            "GROUP BY blog_entries.Title, blog_entries.entryId, blog_entries.date, author " +
            "ORDER BY Date DESC;"
            );
        foreach ( DataRow row in oDt.Rows )
        {
            entries.Add(new BlogEntry(row["entryId"].ToString(), (string)row["author"], row["nbrComments"].ToString(),
                (string)row["entry"], (string)row["date"].ToString(), "Uncategorized"));
        }
        entries.Sort(new EntryComparer(_sortExpression));
        return entries;
    }

    //not done - use relational datasets
    public List<BlogEntry> GetEntriesByMonth(string monthNumber)
    {
        List<BlogEntry> lst = new List<BlogEntry>();
        return lst;
    }

    //not done - use relational datasets
    public List<BlogEntry> GetEntriesByAuthor(/*Author a*/)
    {
        List<BlogEntry> lst = new List<BlogEntry>();
        lst.Add(new BlogEntry());
        return lst;
    }

    //not done
    public static DataSet GetEntry(int id)
    {
        DataAccessLayer oDal = new DataAccessLayer();
        IDbCommand objCmd = oDal.CreateCommand();
        objCmd.CommandText = "SELECT blog_entries.entryId, blog_entries.title, blog_entries.date, " +
           "blog_entries.entry, blog_entries.commenting " +
           "FROM blog_entries WHERE entryId = '@entryId' ORDER BY blog_entries.date ASC";
        System.Data.IDataParameter oParam = objCmd.CreateParameter();
        oParam.ParameterName = "@entryId";
        oParam.Value = id;
        objCmd.Parameters.Add(oParam);
        DataSet ds = oDal.GetDataSet(objCmd);
        return ds;
    }
}

public class EntryComparer : IComparer<BlogEntry>
{
    private string sortColumn;
    private bool reverse;

    public EntryComparer(string _sortExpression)
    {
        reverse = _sortExpression.ToLowerInvariant().EndsWith(" DESC");
        if (reverse) sortColumn = _sortExpression.Substring(0, _sortExpression.Length - 5).ToLowerInvariant();
        else sortColumn = _sortExpression.ToLowerInvariant();
    }

    public int Compare(BlogEntry a, BlogEntry b)
    {
        int retVal = 0;
        switch (sortColumn)
        {
            case "id":
                retVal = String.Compare(a.Id, b.Id, StringComparison.InvariantCultureIgnoreCase);
                break;
            case "title":
                retVal = String.Compare(a.Title, b.Title, StringComparison.InvariantCultureIgnoreCase);
                break;
            case "author":
                retVal = String.Compare(a.Author, b.Author, StringComparison.InvariantCultureIgnoreCase);
                break;
            case "date":
                retVal = String.Compare(a.Date, b.Date, StringComparison.InvariantCultureIgnoreCase);
                break;
            case "category":
                retVal = String.Compare(a.Category, b.Category, StringComparison.InvariantCultureIgnoreCase);
                break;
            case "entry":
                retVal = String.Compare(a.Entry, b.Entry, StringComparison.InvariantCultureIgnoreCase);
                break;
            case "nbrComments":
                retVal = String.Compare(a.NbrComments, b.NbrComments, StringComparison.InvariantCultureIgnoreCase);
                break;
        }
        return (retVal * (reverse ? -1 : 1));
    }
}

Sedan så använder jag ju en färdiggjord komponent som ASP.Net 2.0 kom med, annars hade jag ju enkelt kunna använda den här när jag pillar med code-behind...

Så vad ska jag göra för att få mitt DAL att mer likna ett DAL och mitt BL att mer likna ett BL?

Annars tackar man för berömmet :)

echoSweMedlem sedan nov. 20041 189 inlägg
#18

En liten tanke till - du snackar om att mitt blogentrycomponent för för specifikt - om man utvecklar det mer så att den kan göra en massa olika saker som att köra updates, deletes och inserts också, som är tanken (i och med att gridview stöder det och att jag behöver det för min admin) - och därefter lägger in ett (då) affärslager som hanterar de större sakerna som trackbacks, pingbacks, e-mail notifications etc - blir det bättre struktur på det hela då?

emissionMedlem sedan dec. 19996 721 inlägg
#19

echoSwe skrev:

Godnatt Emission,

Intressant term ;) har inte hört den förut. Så jag ligger och balanserar på något sätt mellan ett DAL och ett BL?

Nej, jag tycker att du demonstrerat ett skolexempel på avgränsning mellan DAL och BL. DAL:et returnerar och arbetar med generella datatyper (DataSet etc.), medan BL jobbar med dina specifika "affärsobjekt".

echoSwe skrev:

Ett DAL - tanken är att man ska kunna använda ett till alla projekt - är inte interfaces (IDbCommand etc) vägen som man får gå då?

Jo, om det är ett så generellt lager som man vill ha så är det helt klart användbart, men det blir låg abstraktion. Jag säger absolut inte att det är fel att göra så, men man måste veta vad man får..

echoSwe skrev:

Ett affärslager har självklart hand om mer avancerade saker också, som att kolla transaktioner, kommunicera med externa tjänster/webservices och bygga upp collections till de presentationslager som behöver dem. Är det inte typ det som jag har gjort här?

Jo, men här har du fört in SQL i affärslagret (vilket ju var själva ursprungsfrågan). Abstraktionen är därmed bortblåst. Däremot är det ju ett smidigt sätt att kommunicera med databasen på, så lösningen i sig har jag inget problem med.

En liten tanke till - du snackar om att mitt blogentrycomponent för för specifikt

Det tycker jag i och för sig, men det jag anmärkte på var att du använde interfacen IDbConnection osv. inne i en MySQL-specifik klass, helt i onödan. Eftersom varken kommandon eller kopplingar exponeras publikt så finns det ingen anledning att tratta in dem i interface-instanser.

echoSweMedlem sedan nov. 20041 189 inlägg
#20

Då tycker jag att ni har besvarat alla mina frågor som jag har för tillfället - dyker det upp nya så frågar jag :). Tack cyprys och emission.

149 ms totalt · 3 externa anrop · v20260731065814-full.30151723
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
146 ms — hämta tråd, inlägg och bilagor (db)