webForumDet fria alternativet

Datalager... klassuppbyggnad.

.NETur .NET

24 svar · 655 visningar · startad av Engine^

Medlem sedan dec. 20003 887 inlägg
Frågan#1

Satt just och läste i tråden Databashantering och jag blev väldans nyfiken på hur renholms struktur ser ut. Jag har som vanligt när jag kommer igång med något nytt gett mig på alla fronter på en gång ;) Tänkte ivarjefall att jag ska bygga upp en finfin datahanteringsklass nu, men jag är inte så sugen på att börja fel.

Har börjat lite lätt med klassen just nu (börjat om igen, för att vara helt ärlig ;))

engine.servehttp.com.cs

using System;
using System.Data;
using System.Data.OleDb;

public class cData
{
  OleDbConnection m_objConn;
  OleDbDataAdapter m_objSQL;

  string m_strConnection;
  string m_strSQL;

  public cData() {}
  public cData(string strSQL) {}
  public cData(string strConnection) {}
  public cData(OleDbConnection objConn) {}

[blue]  .
  .
  . blabla... metoder och sånt[/blue]
}

Nu kom jag på att jag måste fråga några saker...

1. Bör man använda properties (get, set) eller metoder för att hämta värden?
2. Borde jag egentligen göra ett namespace för det här. Jag hade bara tänkt att kompilera det här och stoppa in den i /bin. Går det inte att använda sig av using cData om jag inte gör ett namespace av det hela?

Medlem sedan apr. 20012 266 inlägg
#2

Flyttas till .NET-forumet då detta inte är en specifik ASP.NET fråga.

Nu till svaren!
1. Beror lite på omständigheterna, men jag skulle använda properties för att hämta värden i en klass som fått sina värden vid instansieringen (t.ex om man hämtar värderna från en databas). Om du vill retunera t.ex. en DataReader skulle jag göra det via en funktion från mitt datalager. Denna DataReader kan sedan användas från en business klass som jag skrev tidigare om.

2. För att få struktur på det hela brukar det vara smidigt att dela in klasserna i olika namespace. T.ex. Renholm.Test.Data, Renholm.Test.UI. Sedan kan man välja om man vill kompilera dessa till en varsin assembly eller lägga dem i samma.

Medlem sedan maj 20012 812 inlägg
#3

Om du skall bygga en dataaccess klass så bör du använda dig av interfacen IConnection istället för OleDbConnection, vilket gör det betydligt enklare att byta till en annan databas bara genom att ändra i din connectionstring.

Glöm inte bort att din dataaccess klass vill kunna hanter Transactioner, det gör det lite kluddigare när man skall öppna och stänga sin connection till databasen, eftersom om man har en Transaction så måste den använda samma connection som måste vara öppen en längre tid.

Sedan finns det ingen större anledning till att din databasklass skall kunna instansieras flera gånger. Så gör den static eller en singelton. Eller var lat och använd MS Application Block :) Man lär sig dock mest på att göra saker själv, det är därför som jag alltid uppfinner hjulet en gång till, för att hela tiden göra det lite bättre.

- M

Medlem sedan apr. 20012 266 inlägg
#4

Bara så att det inte blir något missförstånd så vill jag påpekade att det jag sa angående instansiering inte gällde dataobject utan affärsobjekt.

Angeånde transaktioner. Finns det något smart sätt att bygga det på? Om man t.ex har en varukorg samt ett order objekt. Dessa kontrolleras av olika dataklasser då funktionerna för de båda objekten är separerade för att få någon form av struktur. Om jag t.ex vill slutaföra ett köp, då måste jag uppdatera orderobjektet med ny datan och ta bort det som låg varukorgen. Hur kan man använda transaktioner här? Självklart kan man lösa det med en funktion som gör just detta från en dataklass. Men hur löser man det på snyggast sätt om man måste anropa två funktioner i olika objekt som man vill ska köras under en transaktion? Förslag, funderingar?

red... sista stycket

Medlem sedan dec. 20003 887 inlägg
#5

Ahh! Mycket informativa svar. Jag tackar och bockar.

Nog för att det var teori jag var ute efter, så måste jag säga att det blev väldans teoretiska svar :)

IConnection... bör jag använda mig av det för att klassen ska vara mer skalbar (mellan olika DBMS, om jag förstår dig rätt Gladh)? Det innebär ju dock att jag måste fördjupa mig i det också (vilket jag egentligen inte har något emot :OO).

Transaktioner... är inte det beroende av den "dataserver" man använder? Som du skrev, renholm, så menar du väl att transaktionen ska vara "beständig över flera sidor" - Applicational transaction tror jag det kallas. Jag har inte bråkat med transaktioner så mycket att jag kan svara på hur det fungerar, men jag har upplevt helskumma resultat när nästlingen av transaktioner har överstigt två nivåer (i OleDb-kopplingar mot en Accessdatabas).

Nåja, ni verkar ju vara tillräckligt bevandrade inom .NET för att faktiskt kunna ge ett exempel på svar på mina frågor. Vad gäller första svaret var kanske min fråga luddigare än vad svaret var... min plan var/är inte att returnera objekt via properties (exmpelvis adaptrar och datasets osv.) utan t.ex. ConnectionString, RecordsAffected och liknande.

Vad gäller fråga två, så blev jag inte klok på ditt svar renholm. Går det att jämföra namespaces i C# på något sätt med headerfiler i C++? Det jag är ute efter att få grepp om är huruvida man kan skapa en dll (som jag i det här fallet tänkte göra med min dataklass), som endast innehåller klasser och inga namespaces och kunna referera till dessa klasser via using-direktivet. (?)

Måste jag baka in klassen in ett namespace för att kunna referera den via using?

Medlem sedan juni 20022 599 inlägg
#6

Går det att jämföra namespaces i C# på något sätt med headerfiler i C++?

Nej, det tycker jag inte egentligen. Namespaces är bara ett smidigt sätt att gruppera typer/klasser i en logisk hierarki för att få överblick och undvika namnkrockar. De har ingen funktionell betydelse.

huruvida man kan skapa en dll .. som endast innehåller klasser och inga namespaces

Ja, det borde gå utmärkt att göra en dll av denna kod:

	public class Class1
	{
		public Class1()
		{
		}
	}

	public class Class2
	{
		public Class2()
		{
		}
	}

Nu kan man ju hävda att den innehåller två "namespaces": Class1 och Class2 eftersom dessa klasser skulle kunna innehålla inre klasser som man refererade till via punktnotation. Det beror alltså på om man räknar klassnamnet till namespacet eller inte.

Måste jag baka in klassen in ett namespace för att kunna referera den via using?

"using" är bara ett smidigt sätt att kunna använda ett förkortat skrivsätt i editorn. Själva refereringen till dll:en sker via kompilatorväxlar. Om hela det fullt kvalificerade klassnamnet bara består av ett led så finns det ju inget att förkorta och "using" blir ganska meningslös.

Medlem sedan dec. 20003 887 inlägg
#7

Då är det mer eller mindre som jag tänkte.

"using" undrade jag om, eftersom att jag ska använda denna klass i ett ASP.NET-projekt jag fifflar med just nu och det är en och annan codebehindfil som kommer att vilja komma åt denna dataklass.

Medlem sedan dec. 20003 887 inlägg
#8

Angående using talade ASP.NET snällt om för mig att det endast går att använda namespaces om man vill importera referenser till klasser från en DLL (om det inte går via kompileringsväxlar på något mystiskt sätt... niko?). Nåja, det gör inte mig så mycket och det får jag väl anse vara svaret på min andra fråga.

Nåja... jag har en tredje fråga såhär helt oförhappandes ;)
Jag skrev ihop en metod som returnerar ett dataset och började fundera över om klassen ska innehålla en datasetmedlemsvariabel eller om metoden ska skapa sitt eget dataset och returnera det. Jag tycker att jag bör ha det som en medlemsvariabel, så att jag kan köra en Dispose på det i klassens destruktor. Min fundering är alltså egentligen över hur mycket kontroll man har över resurser, som klassen allokerar...

MSDN skrev:

Using Destructors to Release Resources
In general, you should not be as concerned about memory management as you would with C++. This is because the .NET Framework garbage collector implicitly manages the allocation and release of memory for your objects. However, when your application encapsulates unmanaged resources such as windows, files, and network connections, you should use destructors to free those resources. When the object is eligible for destruction, the garbage collector runs the object's Finalize method.

Eller sköter .NET det här åt mig helt automagiskt?

Medlem sedan juni 20022 599 inlägg
#9

Engine^ skrev:

Angående using talade ASP.NET snällt om för mig att det endast går att använda namespaces om man vill importera referenser till klasser från en DLL (om det inte går via kompileringsväxlar på något mystiskt sätt... niko?). Nåja, det gör inte mig så mycket och det får jag väl anse vara svaret på min andra fråga.

Nu kanske vi pratar förbi varandra. Vad jag menade var att:

Engine^.DatabasPrylar.EnTypisktBraigDBConnection c = new Engine^.DatabasPrylar.EnTypisktBraigDBConnection();
// osv ..

är helt likvärdigt med:

using Engine^.DatabasPrylar;
EnTypisktBraigDBConnection c = new EnTypisktBraigDBConnection();
// osv ..

den sista är bara ett sätt att spara tangenttryckningar. Om klassen finns i en extern dll så måste du ange denna vid kompilering/länkning vilken metod du än använder.

Medlem sedan dec. 20003 887 inlägg
#10

Ok. Det var jag som missuppfattade dig :)

Medlem sedan juni 20022 599 inlägg
#11

För att förtydliga ytterligare:

Jag menar alltså att det inte går att jämföra header-filer med using.

Inkludering av header-filer orsakar en inläsning av kod, vilket using inte gör.

using x.y.z;

Är samma sak som att säga till kompilatorn:

"Om du trillar på ett klassnamn vars definition du inte kan hitta nånstans, prova då att slänga på "x.y.z." framför det."

Medlem sedan dec. 20003 887 inlägg
#12

Jag kör vidare i den här tråden, eftersom det fortfarande handlar om samma klass och uppbyggnaden av den. Tänkte visa lite vad jag menade med medlemsvariabler och destruktion. Jag har följande metod för att visa lite data...

public DataView GetDataView(string strTable)
{
  DataSet objDataSet = new DataSet();
  OleDbDataAdapter sqlCmd = new OleDbDataAdapter(m_strSQL, m_objConn);
		
  sqlCmd.Fill(objDataSet, strTable);

  return (DataView) objDataSet.Tables[strTable].DefaultView;
}

Vad händer med sqlCmd, när metoden är klar? Samt kan det vara mer praktiskt eller bättre/sämre att ha objDataSet och sqlCmd som klassmedlemmar istället för att deklarera dom i klassen (på samma sätt som jag gjort med mitt Connectionobjekt m_objConn)?

Medlem sedan dec. 20003 887 inlägg
#13

Vad frustrerande att det var så ensamt i .NET-forumet just nu då ;)

Nåja, jag postar vidare och väntar glatt efter kommentarer...

using System;
using System.Data;
using System.Data.OleDb;

namespace nsData
{
  public class cData
  {
    OleDbConnection m_objConn;
    OleDbDataAdapter m_objSQL;

    string m_strConnection;
    string m_strSQL;

    public string ConnectionString
    {
      get {return this.m_strConnection;}
      set {this.m_strConnection = value;}
    }
 
    public string SqlString
    {
      get {return this.m_strSQL;}
      set {this.m_strSQL = value;}
    }

    public cData() {}
    public cData(string strConnection)
    {
      m_objConn = new OleDbConnection(strConnection);
    }

    ~cData()
    {
      m_objConn.Dispose();
      m_objSql.Dispose();
    }

    public DataView GetDataView(string strTable)
    {
      DataSet objDataSet = new DataSet();

      m_objConn = new OleDbConnection(m_strConnection);
      m_objSql = new OleDbDataAdapter(m_strSQL, m_objConn);

      m_objSql.Fill(objDataSet, strTable);

      return (DataView) objDataSet.Tables[strTable].DefaultView;
    }
  }
}

Nu vill jag inte göra så mycket mer, eftersom jag känner att fortsätter jag som jag börjat nu blir resten antingen rätt eller så blir allt fel. (Grrr... jag gillar inte den okunskap jag besitter om detta x()

Bör jag tänka om någonstans? (under tiden tar jag nog kollar på det här med IConnection...)

Medlem sedan maj 20012 812 inlägg
#14

renholm:

Jag förstår inte riktigt hur du har byggt upp din struktur :(

Jag kan dock berätta hur jag hade gjort/gör.

Först har jag en DataAccess klass som är en static klass som (för enkelhetens skull) har 4 publica metoder.

public static void Execute(SqlCommand, Transaction){}
public static Transaction BeginTransaction()
public static void CommitTransaction(Transaction){}
public static void RollbackTransaction(Transaction){}

Jag har sedan en DataManager klass som också är static som fyller mina objekt med data från databasen

public static void Load(object, Transaction){}
public static void Save(object, Transaction){}
public static void Delete(object, Transaction){}

Sedan så har jag mina olika affärsobjekt säg COrder och CVarukorg. Genom klassen DataManager så kan jag Ladda, spara och delete ett objekt från/till databasen typ så här.

COrder oOrder = new COrder();
oOrder.StoreProcedureName = "SP_SaveOrder";
oOrder.ArtikelNr = 1;
.
.
.
DataManager.Save(oOrder);

Med denna kod så skapar jag ett nytt Order objekt berättar att min storeprocedure som skall köras när jag vill spara/ladda/deleta objektet från databasen. Fyller mitt objekt med data och skickar sedan detta objekt till DataManager klassen sparar ner mitt objekt till databasen.

Om jag nu vill ta mitt order objekt och uppdatera min varukorg med den datan, och sedan ta bort order objektet, så vill det ju till att uppdateringen av varuobjektet gått bra annars vill jag inte ta bort orderobjektet, och om jag inte kan ta bort oderobjektet så vill jag inte uppdatera varukorgen.

SqlTransaction oTransaction = DataAccess.BeginTransaction();
try{
 //-- lägger orderobjektet till varukorgen
 oVaruKorg.Add(oOrder);

 //-- okej ta bort orderobjektet
 DataManager.Delete(oOrder, oTransaction);

 //-- updatera vår varukorg
 DataManager.Save(oVaruKorg, oTransaction);

 //-- okej allt gick bra, commita vår transaction
 DataAccess.CommitTransaction(oTransaction);

}catch(Exception e){
DataAccess.RollbackTransaction(oTransaction);
}

Nu så kommer allt gå bra, eller så kommer allt rullas tillbaka i databasen, du får manuellt ta bort objektet från varukorgen om det har lagts till och man sedan fick ett fel, så att transactionen inte blir commitat.

Transaction över flera olika databaser kan man inte lösa i koden utan måste så fall blanda in COM+ och transaktionshantering där.

- M

Medlem sedan maj 20012 812 inlägg
#15

IConnection... bör jag använda mig av det för att klassen ska vara mer skalbar (mellan olika DBMS, om jag förstår dig rätt Gladh)? Det innebär ju dock att jag måste fördjupa mig i det också (vilket jag egentligen inte har något emot ).

Inte bara IConnection, utan även ICommand, IDataAdapter, IDataReader osv osv.

Allså alla klasser som heter Sql eller OleDb så har de ett interface och om du använder det så behöver du aldrig bekymmra dig om det är en Access, SqlServer, MySql, Oracle databas du kopplar dig mot.

- M

Medlem sedan dec. 20003 887 inlägg
#16

Herrejösses då... :)

Jag fick plötsligt känslan av att jag har väldigt långt kvar innan denna dataklass är någorlunda användbar ;)

Jag har inte tänkt i affärsobjektsbanor, men jag inser ju att det är den vägen man jag vandra. Dags att göra om och göra färre misstag denna gång :) Denna gång blir det att tänka dataaccess, datahantering och dataobjekt (affärsobjekt?)

En snabb fråga dock... hur i hela friden deklarerar man en klass som static? class har väl ingen sån modifier...

Medlem sedan apr. 20012 266 inlägg
#17

Gladh: Har princip som dig fram till, DataManager där jag ärver från min basklass som ger den grundläggande funktionaliteten. Denna datamanager "anropar" jag sedan från t.ex cOrder då jag vill lägga till/ändra/ta bort.

ex.

// posta ett meddelande från en webbsida
AffärsObjektMeddelande msg = new AffärsObjektMeddelande();
msg.Properties = tbName.Text;
// osv...
msg.Create();
public bool Create()
{
   // datamanager anropas.
   datamanger.Create(this.name, this.msg, this.ip /* osv */);
}
   // ex på datamanager
public int UpdateAddon(int id, string articlenumber, string name, string description, double price, double discount, string quantity, string[] quantityvalues, string culture, string parametername)
		{
			SqlParameter[] parameters = 
			{
				new SqlParameter("@id", SqlDbType.Int, 4),
				new SqlParameter("@articlenumber", SqlDbType.NVarChar, 10),
				new SqlParameter("@name", SqlDbType.NVarChar, 50),
				new SqlParameter("@description", SqlDbType.NText),
				new SqlParameter("@price", SqlDbType.Float),
				new SqlParameter("@discount", SqlDbType.Float),
				new SqlParameter("@quantity", SqlDbType.NVarChar, 10),
				new SqlParameter("@quantityvalues", SqlDbType.NVarChar, 50),
				new SqlParameter("@culture", SqlDbType.NVarChar, 5),
				new SqlParameter("@parametername", SqlDbType.NVarChar, 50)
			};

			parameters[0].Value = id;
			parameters[1].Value = articlenumber;
			parameters[2].Value = name;
			parameters[3].Value = description;
			parameters[4].Value = price;
			parameters[5].Value = discount;
			parameters[6].Value = quantity;
			string values = ArrayToString(quantityvalues);
			if (values == null)
				parameters[7].Value = System.DBNull.Value;
			else
				parameters[7].Value = values;
			parameters[8].Value = culture;
			if (parametername == null)
				parameters[9].Value = System.DBNull.Value;
			else
				parameters[9].Value = parametername;

			int rowsaffected;
			RunProcedure("sp_store_UpdateAddon", parameters, out rowsaffected);
			return rowsaffected;
		}

Dock har jag funderat på din metod Gladh, den verkar smidig då man slipper databaser helt i sin affärsklass.

Medlem sedan maj 20012 812 inlägg
#18

En snabb fråga dock... hur i hela friden deklarerar man en klass som static? class har väl ingen sån modifier...

Har den säkert, man jag gör mina metoder statics alltså behöver inte min klass vara det, men det kan man säkert, har aldrig tänkt eller funderart på det :(

Renholm.
Man måste ha en viss koppling mellan sitt objekt och sin databas, och det är så att man vet vilket fält i klassen skall kopplas till vilket fält i databasen, detta har jag löst med Attributes, man kan använda sig av config filer istället, eller till och med lägga det i databasen som en speciell tabel. Det är en smak sak, jag gillar attributen då jag har allt samlat på ett ställe.

Fördelen jag har är att jag slipper UpdateAddon vilket betyder att om jag ändrar i min databas så behöver jag bara ändra på 1 ställe och det är antalet fält/parameters i min klass, jag behöver inte ändra i någon funktion som bygger ihop mina parameters eftersom allt detta sker dynamiskt.

så här kan min klass se ur:

public class COrder{
 [SqlParameterLoad("@ID")]
 [SqlParameterSave("@ID")]
 [SqlParameterDelete("@ID")]
 [DatabasField("ID",0)]
 private long _ID;

}

Här har jag ett fält som heter _ID, till detta fält har jag 4 attribute, de 3 första berätta till vilken parameter i SP som detta fält skall sätta sitt värde. Det sista attributet berättar vilket fält i tabellen som detta fält skall få sitt värde ifrån.

Det betyder att när jag lägger till ett nytt fält i databasen (Artikelnummer) så lägger jag till ett nytt fält i min klass och sätter attribute på detta fält.

[SqlParameterSave("@ArtikelNr")]
[Databasfield("Artikelnummer",0)]
private long _ArtikelNumber;

och vips så kan jag spara och läsa till/från databasens artikelnummer.

0 som jag skickar med berättar vad som är NULL eftersom valutypes så som int,long inte kan tildelas värdet NULL så måste man omvandla NULL i databas till ett värde i klassen och här vill jag omvandla NULL till 0

- M

Medlem sedan apr. 20012 266 inlägg
#19

Ok, verkar relativt smidigt. Är SqlParameterSave specifik för SqlClient? Hur fungerar detta med Stored Procedures? Har du någon kod som visar hur din DataManager fungerar?

Medlem sedan dec. 20003 887 inlägg
#20

Det känns som om ni två herrar ligger ljusår före mig ;) Har ni något länktips, som behandlar detta (till synes stora) område? Jag måste nog börja helt från grunden även om jag förstår principerna - det är lika bra att studera innan programmeringen fortsätter något mer.

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