webForumDet fria alternativet

DAL fråga

.NETur .NET

10 svar · 729 visningar · startad av CatZ

CatZMedlem sedan jan. 20022 440 inlägg
#1

Jag funderar på att skicka med någon typ av parameter för att välja SQL-sats i min DAL så jag kan återanvända mera men undrar om man verkligen ska göra så...

Skulle vilja sy ihop något som nedan

public List<News> GetNews(string SelectType)
{
	string strSql;
	switch(SelectType)
	{
		case "ByID":strSql = SqlGetByID;
		case "Top1":strSql = SqlGetTop1;
		case "Top5":strSql = SqlGetByID;
		case "List":strSql = SqlGetNewsList;
	}

	DataTable dt = GetTable(strSql);
	//Declare the list
	List<News> News = new List<News>();
}

Får man göra så? :)

GladhMedlem sedan maj 20012 812 inlägg
#2

CatZ skrev:

Får man göra så?

Du får göra precis som du vill, det är ju din kod ;)

Problemet blir att du kommer göra en sörens massa SQLsträngar eftersom du måste göra din switchsats för varje typ som du vill hämta data ifrån.

Jag har i min OR-Mapper endast 3 möjligheter.

  1. Hämta Alla.
  2. Hämta efter ID
  3. Exekvera ett Command.

Jag började som du med att försöka få till det med Joins och Top och alla möjliga varianter, men har nu tagit bort det och har 2 fasta metoder, Hämta Alla och Hämta efter ID. Sedan har jag möjligheten att exekvera ett command, som då kan exekvera SP eller SQL och där kan jag hantera alla möjligheter.

Typ så här:

Article article = EntityMapper.Load<Article>(id);

List<Article> articleList = EntityMapper.Load<Article>();

List<Article> articleList = EntityMapper.ExecuteCommand<Article>(command);

Det viktiga är att du slipper sitta och mappa data till dina entiteter i varje projekt, det tar lång tid att göra, där kommer ORMappern in. Skicka in vilket objekt du vill till Load() methoden och du får tillbaka det fyllt med data från databasen.

Så här ser det ut i mitt "DAL" i mitt projekt som jag håller på med just nu:

                //-- Create command to call SP
                IDbCommand command = new SqlCommand();
                command.CommandType = CommandType.StoredProcedure;
                command.CommandText = "dbo.GetCategories";

                SqlParameter parameter = new SqlParameter("@LCID", SqlDbType.Int);
                parameter.Value = lcid;
                command.Parameters.Add(parameter);

                TSList<Category> itemList = _EntityMapper.ExecuteCommand<Category>(command);
                return itemList;
                //-- Create command to call SP
                IDbCommand command = new SqlCommand();
                command.CommandType = CommandType.StoredProcedure;
                command.CommandText = "GetColors";

                SqlParameter parameter = new SqlParameter("@LCID", SqlDbType.Int);
                parameter.Value = lcid;
                command.Parameters.Add(parameter);

                TSList<Color> itemList = _EntityMapper.ExecuteCommand<Color>(command);
                return itemList;

Som du märker så är det exakt samma metod som jag anropar i min EntityMapper, men får tillbaka olika typer beroende på vilken typ jag skickar med till entitymappern, och här spara jag massor med tid genom att jag slipper sitta och mappa mellan datan och objektet.

- M

CatZMedlem sedan jan. 20022 440 inlägg
#3

Nu är det helt snurrigt i huvudet... Ska titta på det du skriver imorn när jag vaknar. Måste iväg och träna lite innan mitt tråkiga jobb :)

Såhär ser det ut nu

const string SqlGetAll = "SELECT News.ID, News.Title, News.Content, News.nDate, Users.FullName, Users.Email FROM Users INNER JOIN News ON Users.ID = News.UserID;";
	const string SqlGetByID = "SELECT News.ID, News.Title, News.Content, News.nDate, Users.FullName, Users.Email FROM Users INNER JOIN News ON Users.ID = News.UserID WHERE News.ID = ? ORDER BY News.nDate DESC;";
	const string SqlGetTop5 = "SELECT TOP 5 News.ID, News.Title, News.Content, News.nDate, Users.FullName, Users.Email FROM Users INNER JOIN News ON Users.ID = News.UserID ORDER BY News.nDate DESC;";
	const string SqlGetTop1 = "SELECT TOP 1 News.ID, News.Title, News.Content, News.nDate, Users.FullName, Users.Email FROM Users INNER JOIN News ON Users.ID = News.UserID ORDER BY News.nDate DESC;";
	const string SqlGetNewsList = "SELECT News.ID, News.Title, News.nDate, Users.FullName FROM Users INNER JOIN News ON Users.ID = News.UserID ORDER BY News.nDate DESC;";

Skapa skapa IList

public List<News> GetNews(string SelectType)
{
	string strSql;
	switch (SelectType)
	{
		case "News":
			strSql = SqlGetAll;
			break;
		case "ByID":
			strSql = SqlGetByID;
			break;
		case "Top1":
			strSql = SqlGetTop1;
			break;
		case "Top5":
			strSql = SqlGetTop5;
			break;
		case "List":
			strSql = SqlGetNewsList;
			break;
		default:
			strSql = SqlGetAll;
			break;
	}
	// Create the datatable
	DataTable dt = GetTable(strSql);

	// Declare the list
	List<News> News = new List<News>();

	// Iterate through the datatable
	for (int i = 0; i < dt.Rows.Count; i++)
	{
		// Declare each row
		News news = new News();

		// Bind the datatable values to the news object.
		news.ID = (int)dt.Rows[i]["ID"];
		news.nDate = (DateTime)dt.Rows[i]["nDate"];
		news.Title = dt.Rows[i]["Title"].ToString();
		news.FullName = dt.Rows[i]["FullName"].ToString();

		if (SelectType != "List")
		{
			news.Content = dt.Rows[i]["Content"].ToString();
			news.Email = dt.Rows[i]["Email"].ToString();

		}

		//Adds the row to the collection list
		News.Add(news);
	}
	return News;
}//End function

skapa datatable och lägg till parameter

public DataTable GetTable(string strSql)
{
	using (OleDbConnection dbCon = new OleDbConnection(ConfigurationManager.ConnectionStrings["wbBas"].ConnectionString))
	{

		OleDbCommand dbCmd = new OleDbCommand(strSql, dbCon);

		// Add parameters if any
		if (_paramsList != null)
			foreach (OleDbParameter p in _paramsList)
				dbCmd.Parameters.Add(p);

		// Open connection and fill the datatable
		dbCon.Open();
		DataTable dt = new DataTable("News");
		dt.Load(dbCmd.ExecuteReader());

		// Clean up objects
		dbCon.Close();
		dbCmd.Dispose();

		// Return the datatable
		return dt;
	}
}

public void AddOleDbParameter(string ParamName, OleDbType DbType, object Value)
{
	// If the parameter list isnt created do so
	if (_paramsList == null)
		_paramsList = new List<IDataParameter>();
	{
		// Create the parameter
		OleDbParameter param = new OleDbParameter(ParamName, DbType);
		param.Value = Value;

		// Add the parameter to the list
		_paramsList.Add(param);
	}
}

och sedan för att anropa mitt dal skulle jag då göra följande....

public List<News> GetNewsByID(int id)
{

	NewsDAL dal = new NewsDAL();
	dal.AddOleDbParameter("p1", OleDbType.Numeric, id);
	return dal.GetNews("ByID");

}

Det är säkert inte optimalt men det känns smidigt vid första anblicken... Som sagt jag hinner inte spåna på ditt inlägg just nu men Jag ska titta på det imorn när jag inte måste iväg och jobba :)

Ni får gärna kommentera mera på det här inlägget. Jag förstår inte de grundläggande principerna i .net och C# än men jag lär mig så all hjälp att göra rätt mottages tacksamt.

Gladh, jag måste verkligen inte göra så som jag postade här nu jag försöker bara komma fram till vilket som blir bäst.

JonMedlem sedan juli 20011 304 inlägg
#4

Jag tycker att det är helt ok att göra som du gör om du inte har väldans massa olika frågor.

Jag skulle personligen göra fler metoder istället för att switcha en sträng, typ

public List<News> GetAllNews();
public List<News> GetNewsById(long id);
public List<News> GetTop5News();
public News GetTop1News();

Och sen låta mitt DAL skapa och hantera Parametrarna. Mest för att jag tycker att det blir tydligare samt att du får kompileringsfel direkt om du ropar felaktigt på en metod men inte om du skickar namnet på den som en parameter. Sedan vill jag astrahera mitt DAL från användaren av det, denna ska ge katten i hur parametrar skapas och vilka datatyper som de behöver använda ;) I ditt fall speccar du även att det är en oledb-parameter, dvs du kopplar ihop ditt Affärlager hårt med titt Datalager och om du vill ändra databas behöver ditt affärslager veta om det. Ditt DAL blir inte så återanvändbart och inte så skalbart.

Vad det gäller att bygga sql:en mer dynamiskt så är det så att vill man ha fullständig flexibilitet får man bygga ett eget "frågespråk" som genererar sql.
Det finns i de flesta bättre o/r mappers. Det är dock ganska tidsödande.

Lite offtopic:
Microsoft har själva tagit fram Linq som är ett standardiserat sätt att fråga objekt och listor av objekt efter kriterier. Det ingår i senaste versionen av .net. Med ramverket Linq to sql kan man även sköta o/r mappning, även om ramverket har ett par begränsningar. Jag har inte hunnit sätta mig in i det helt och hållet men vid en första anblick så känns det som ett bra alternativ vid mindre projekt.

Lite mer offtopic:
Jag tycker dock att det är mycket lärorikt att göra det själv för att få förståelse för teknikerna och tankegångarna. Bra strävan av att göra saker och ting "rätt", även om man efter ett tag märker att det finns ganska få absoluta sanningar och många olika åsikter. Jag tycker att man måste bestämma sig för vissa utgångspunkter i sin kodning, t.ex: Jag vill använda DDD och o/r mappning, då är det mycket lättare att göra "rätt" utifrån dessa förutsättningar.
Vill man däremot gör "rätt" utifrån MS-klicka, dra-adapters-auto-dataset-binding så är det helt andra "rätt" man far efter :)

Med det sagt känns det som om du är ute efter att använda en oo-modell och ha en enklare egenbyggd o/r mappning i botten. Titta gärna lite på olika bloggar, böcker och forum efter DDD och OO i allmänhet och känn efter hur o/r mappning fungerar och fortsätt att ställa frågor här i så fall.

GladhMedlem sedan maj 20012 812 inlägg
#5

Catz. Dina 2 metoder:

public List<News> GetNews(string SelectType)
och
public DataTable GetTable(string strSql)

Är kod som jag har lagt i min EntityMapper, det bästa är att jag kan återanvända den i alla projekt som jag bygger, även om det inte finns någon News entity i det projektet. Ditt "DAL" som du bygger blir specifikt för varje nytt projekt som du påbörjar, vilket gör att du inte kan återanvända din kod lika mycket som jag kan.

Följande kod hos dig:

public List<News> GetNewsByID(int id)
{

	NewsDAL dal = new NewsDAL();
	dal.AddOleDbParameter("p1", OleDbType.Numeric, id);
	return dal.GetNews("ByID");

}

Skulle motsvara följande kod hos mig.

public News GetNewsByID(int id){
 return new EntityMapper(ConnectionString).Load<News>(id);
}

Och skulle jag vilja köra någon av din specifika SQL satser så blir det.

                //-- Create command to call SP
                IDbCommand command = new SqlCommand();
                command.CommandType = CommandType.Text;
                command.CommandText = "SELECT TOP 1 News.ID, News.Title, News.Content, News.nDate, Users.FullName, Users.Email FROM Users INNER JOIN News ON Users.ID = News.UserID ORDER BY News.nDate DESC;";

                return  _EntityMapper.ExecuteCommand<News>(command);

Fördelen är ju som sagt att jag slipper skriva de 2 tunga metoder där du sköter all mappning...

Däremot så måste min News klass innehålla metadata som berättar vilka kolumner i databasen som skall fylla vilka variabler i klassen med data, det bästa sättet att göra det på är att skapa en xml-fil och lägga utanför projektet som O/RMapperna läser. Jag har inte riktigt orkat göra det än, så jag har bara satt specifika attribute på variablerna som skall ha värden ifyllda...

- M

- M

CatZMedlem sedan jan. 20022 440 inlägg
#6

Det börjar klarna, men jag kommer att göra egna mappningar för tillfället.

På tal om ingenting alls, hur ska mina namespace se ut? Ska man köra på samma namespace på alla de olika projekten förresten?

Jag vill ju inte behöva allt för långa anropsnamn, är det vettigt att ge alla samma namespace eller ska dal tex heta huvudnamespace.dal eller hur gör man med det för att slippa problem när allt kompileras?

CatZMedlem sedan jan. 20022 440 inlägg
#7

Och förresten så bör jag väl om jag förstått det hela rätt ha fem stycken projekt.

1. Entities
2. EntityMapper
3. DaL
4. BLL
5. UI

Då har man verkligen brutit ner alltihop i microskopiska partiklar och allting jag gör blir lättare att återanvända, lättare att korrigera och göra tillägg på.

Jag validerar input i BLL, säger åt dal att hämta nåt som i EntityMapper mappas gentemot mina entiteter.

Om jag lyckas strukturera upp det så borde det bli väldigt lätt att plocka ut valda delar och återanvända/anpassa till framtida projekt.

GladhMedlem sedan maj 20012 812 inlägg
#8

Agnående namespacen så finns det ju massor med olika teorier, men jag brukar döpa mina självförklarande.

[Företag].[SolutionName].[Layer].[Projectname]

exempel:
GladhsDataAb.KillerApp.FrontEnd.Wpf
eller
GladhsDataAb.KillerApp.Shared.Entity
eller
GD.KillerApp.BackEnd.Business
eller
GD.KillerApp.BE.DataAccessLayer
eller
GD.KillerApp.BE.DAL

Du förstår säkert ;)

Sedan tycker jag inte att du behöver både ett DAL och en EntityMapper. Ditt businesslager anropar ditt DAL som hämtar data och mappar det till entiterna. Så Entitymappern är mer något externt projekt som man bara har en referens till och så fall anropar DAL:et denna som i sin tur hämtar data och mappar till entiter. Men ditt projekt "slutar" vid dalet, det går inte djupare än så...

- M

GladhMedlem sedan maj 20012 812 inlägg
#9

CatZ skrev:

Det börjar klarna, men jag kommer att göra egna mappningar för tillfället.

Så fall hade jag flyttat ut mappningen till egna metoder så de inte ligger in i din GetNews() metod.

Dessutom så hade jag skapat 2 metoder för mappningen.

private List<News> MapToNews(DataTable dt)
{
   List<News> itemList = new List<News>();

   foreach(DataRow dr in dt.Rows)
     itemList.Add(MapToNews(dr));

   return itemList;
}

private News MapToNews(DataRow dr)
{
   News item = new News();

   item.id = dr["id"];
   ...
   ...

   return item;
}

För om man då ser här så kommer de flesta mappningsmetoder se nästan likadana ut och en mer generell mappninsmetod verkar mer lockande...

- M

CatZMedlem sedan jan. 20022 440 inlägg
#10

Gladh skrev:

För om man då ser här så kommer de flesta mappningsmetoder se nästan likadana ut och en mer generell mappninsmetod verkar mer lockande...

- M

Det var ju precis dit jag ville komma med hela den här tråden :) Då blir det ultimat! Det jag ville slippa hela tiden var ju att behöva mappa entiteterna på samma vis i 5 olika rutiner...

Gör jag som du skrev så blir ju inte min dal många rader kod...

Tack för hjälpen!

CatZMedlem sedan jan. 20022 440 inlägg
#11

Jon skrev:

Jag skulle personligen göra fler metoder istället för att switcha en sträng, typ

public List<News> GetAllNews();
public List<News> GetNewsById(long id);
public List<News> GetTop5News();
public News GetTop1News();

Ja precis, det ska jag givetvis göra så som du menar.

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