Jag gillar att hålla min kod kort och enkel så därför har jag skapat en metod som jag kan anropa för att redigera eller hämta data från databasen. När jag ska läsa/skriva till databasen direkt via ADO.NET så brukar jag använda en liten statisk utility klass som heter Db med funktioner som tar in delegater så man kan skriva så här: Tack! Det var ett bättre svar än ajg hoppats på. har skrivit om din kod för MySQL men stöter på ett problem. Readern stängs nog när när man disposar connection/command. Verkar omständigt att skapa en klass eftersom jag då måste skapa en klass för varje SQL förfrågan somr eturnerar data. Hur brukar du göra när du ska t.ex. fylla en datarepeater? Använder nästan alltid klasser (entiteter) för data som hämtas från databaser. Sen använder jag ett ORM ramverk så mycket jag kan (NHibernate). Tack Torkel, jag har funderat en hel del på hur jag ska lösa det samt läst en del artiklar om datareader/dataadapter och datasets. Ja det låter som en bra plan. Tack, är säker på att jag läste någonstans att datasets är långsammare men kan inte hitta den artikeln igen. Hur som helst så behöver jag oftast inte hämta några större mängder data. Stötte på ett problem nu, försöker göra någonting sånt här: Hej igen, har använt det här en tid nu och har fungerat jätte bra!Metod för att hämta/redigera mysql databas.
Att redigera databasen är rätt enkelt, jag anropar helt enkelt min metod med
SQLEdit("UPDATE table blah blah");
Men jag kan inte tänka ut någon bra lösning när det gäller att hämta data, just nu har jag en metod som returnerar ett dataset.
DataSet tabledata = SQLSelect("SELECT FROM ...");
Jag läste dock någonstans att datasets är mycket långsammare än att använda datareaders. Men när jag använder en datareader verkar det som att jag inte kan stänga connectionen till databasen när jag är klar.Sv: Metod för att hämta/redigera mysql databas.
Db.Transaction(delegate(IDbCommand cmd)
{
cmd.CommandText = "DELETE FROM LogEntries WHERE DateCreated < @DateCreated";
cmd.Parameters.AddWithValue("@DateCreated", DateTime.Now.Subtract(TimeSpan.FromDays(30)));
cmd.ExecuteNonQuery();
});
Har en del varianter som inte utför frågan i en transaktion, tex Db.Perform() och Db.Perform<ReturnType>() för frågor som returnerar data.
Kod:
public static class Db
{
public delegate void SqlCommandHandler(SqlCommand command);
public delegate T SqlCommandHandler<T>(SqlCommand command);
public static void Perform(SqlCommandHandler handler)
{
Perform<object>(
delegate(SqlCommand cmd)
{
handler(cmd);
return null;
});
}
public static T Perform<T>(SqlCommandHandler<T> handler)
{
SqlConnection connection = null;
SqlCommand command = null;
try
{
connection = new SqlConnection(Settings.ConnectionString);
command = new SqlCommand();
command.Connection = connection;
connection.Open();
return handler(command);
}
finally
{
if (connection != null)
connection.Dispose();
if (command != null)
command.Dispose();
}
}
public static void Transaction(SqlCommandHandler handler)
{
using (SqlConnection connection = new SqlConnection(Settings.ConnectionString))
{
connection.Open();
SqlTransaction tx = connection.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
using(SqlCommand cmd = connection.CreateCommand())
{
cmd.Transaction = tx;
handler(cmd);
}
tx.Commit();
}
catch
{
tx.Rollback();
throw;
}
}
}
}
Exempel på hur man kan använda det när man vill läsa data:
return Db.Perform<string>(
delegate(SqlCommand cmd)
{
cmd.CommandText = "SELECT " + type + " FROM OperationTraces WHERE Id=@Id";
cmd.Parameters.AddWithValue("@Id", id);
return cmd.ExecuteScalar() as string;
});
Det borde vara rätt lätt att ändra så koden ovan fungerar mot mysql.
Anonymous delegates är rätt användbara :)
http://www.codinginstinct.com/2008/04/power-of-delegates.html
/Torkel
Sv:Metod för att hämta/redigera mysql databas.
Felmeddelandet är:
System.InvalidOperationException: Invalid attempt to call FieldCount when reader is closed.
Line 126: Content.DataBind();
Verkar som att Readern stängs, men när händer det?
Min kod ser ut såhär:
<code>
namespace SIMA_DataBase2
{
public static class DB2
{
public delegate void SqlCommandHandler(OdbcCommand command);
public delegate T SqlCommandHandler<T>(OdbcCommand command);
public static void Perform(SqlCommandHandler handler)
{
Perform<object>(
delegate(OdbcCommand cmd)
{
handler(cmd);
return null;
});
}
public static T Perform<T>(SqlCommandHandler<T> handler)
{
OdbcConnection connection = null;
OdbcCommand command = null;
try
{
connection = new OdbcConnection(ConfigurationManager.AppSettings["ConnectionString"]);
command = new OdbcCommand();
command.Connection = connection;
connection.Open();
return handler(command);
}
finally
{
if (connection != null)
connection.Dispose();
if (command != null)
command.Dispose();
}
}
public static void Transaction(SqlCommandHandler handler)
{
using (OdbcConnection connection = new OdbcConnection(ConfigurationManager.AppSettings["ConnectionString"]))
{
connection.Open();
OdbcTransaction tx = connection.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
using (OdbcCommand cmd = connection.CreateCommand())
{
cmd.Transaction = tx;
handler(cmd);
}
tx.Commit();
}
catch
{
tx.Rollback();
throw;
}
}
}
}
}
</code>
Sedan försöker jag fylla en repeater via en ODBC Data Reader.
<code>
public OdbcDataReader GetSiteBlocks()
{
OdbcDataReader SiteBlocks = DB2.Perform<OdbcDataReader>(
delegate(OdbcCommand cmd)
{
cmd.CommandText = "SELECT sb.BlockID, bb.HorizontalPos, bb.Pos FROM SiteBlocks sb INNER JOIN BlockBinder bb ON bb.BlockID = sb.BlockID WHERE bb.PageID = @PageID ORDER BY bb.Pos ASC";
cmd.Parameters.AddWithValue("@PageID", SIMA_Tools.PageName);
return cmd.ExecuteReader();
});
return SiteBlocks;
}
</code>
<code>
public void UpdateContentDisplay()
{
Content.DataSource = GetSiteBlocks();
Content.DataBind();
}
</code>Sv: Metod för att hämta/redigera mysql databas.
Du kan göra så här:
public SiteBlock[] GetSiteBlocks()
{
return DB2.Perform<SiteBlock[]>(
delegate(OdbcCommand cmd)
{
cmd.CommandText = "SELECT sb.BlockID, bb.HorizontalPos, bb.Pos FROM SiteBlocks sb INNER JOIN BlockBinder bb ON bb.BlockID = sb.BlockID WHERE bb.PageID = @PageID ORDER BY bb.Pos ASC";
cmd.Parameters.AddWithValue("@PageID", SIMA_Tools.PageName);
using (IDataReader reader = cmd.ExecuteReader())
{
IList<SiteBlock> blocks = new List<SiteBlock();
while(reader.Read())
{
SiteBlock block = new SiteBlock();
block.Id = (int)reader["BlockID"];
///... etc
blocks.Add(block);
}
return blocks.ToArray();
}
});
}
Om du inte vill skapa en SiteBlock class så kan du istället använda en DataTable, men det rekommenderar jag inte :)
Sv:Metod för att hämta/redigera mysql databas.
Sv: Metod för att hämta/redigera mysql databas.
Det kan tyckas överflödigt att skapa klasser för data som man bara ska visa, men oftast behöver man bygga logik kring datat (båda business logik och presentationslogik), och då är det till stor hjälp att ha riktiga klasser som modellerar informationen. Man kan placera in logik i entiterna, den relaterade business logiken och presentationslogiken blir renare, etc.
Föreslår lite läsning om DDD (Domain driven design):
http://en.wikipedia.org/wiki/Domain-driven_design
http://www.infoq.com/articles/ddd-in-practiceSv:Metod för att hämta/redigera mysql databas.
Det som fick mig att göra mitt val var:
http://msmvps.com/blogs/williamryan/archive/2005/02/26/37015.aspx
Där han skriver bland annat "Performance isn't everything and maintenance and ease of development are every bit as big time issues as performance in most cases."
Jag tror inte att det i dagsläget är nödvändigt för den applikation jag utformar att jag använder mig av entiteter varje gång jag hämtar data. Min applikation består egentligen av 2 skikt, ett publikt gränssnitt och ett admingränssnitt. Det mesta utförs på admin sidan men det är den publika biten som kommer att ha mest besökare och kräva mest prestandaoptimering.
Så jag har valt att följa ditt exempel med entiter för den publika delen men gå med de mer lätthanterliga datatables på admindelen.
Hur som helst så handlar det inte om några större mängder data som hämtas från databasen. Som högst kanske 20 eller 30 rader per förfrågan.
Ifall prestanda blir ett problem i framtiden är jag beredd att lägga ner en del tid på optimering.
Låter det som en bra plan?
JonasSv: Metod för att hämta/redigera mysql databas.
Men valet mellan entiter och dataset/tables har inte mycket med prestanda att göra (dataset kan vara lite minneskrävande). Utan handler mer om underhållbarhet. Om du tycker datasets blir smidigare i admin gränssnittet använd det, men om du behöver skriva logik som bearbetar datat eller utnyttjar datat så rekommenderar jag starkt entiteter :) Sv:Metod för att hämta/redigera mysql databas.
Tack för all din hjälp, den har hjälpt mig mycket och kommer absolut att hjälpa mig med det här och framtida projekt.Sv: Metod för att hämta/redigera mysql databas.
<code> DB2.Perform(
delegate(OdbcCommand cmd) {
cmd.CommandText = "SELECT sb.BlockID, bb.HorizontalPos, bb.Pos FROM SiteBlocks sb INNER JOIN BlockBinder bb ON bb.BlockID = sb.BlockID WHERE bb.PageID = @PageID ORDER BY bb.Pos ASC";
cmd.Parameters.AddWithValue("@PageID", SIMA_Tools.PageID);
Content.DataSource = cmd.ExecuteReader();
Content.DataBind();
}
);</code>
Men av någon anledning ignoreras cmd.AddWithValue, om jag sätter in SIMA_Tools.PageID direkt i sql strängen fungerar det dock.
EDIT: Verkar som att ODBC inte stödjer named parameters. :( Fick istället göra såhär:
<code> cmd.CommandText = "SELECT sb.BlockID, bb.HorizontalPos, bb.Pos FROM SiteBlocks sb INNER JOIN BlockBinder bb ON bb.BlockID = sb.BlockID WHERE bb.PageID = ? ORDER BY bb.Pos ASC";
cmd.Parameters.AddWithValue("?", SIMA_Tools.PageID);</code>Sv:Metod för att hämta/redigera mysql databas.
Men nu har jag stött på ett problem som jag inte förstår alls. Har en metod med koden
<code>
public void AddMenuItems()
{
SIMA_DB.Perform(
delegate(MySqlCommand cmd)
{
cmd.CommandText = "SELECT p.CleanName, p.PublicName, p.PageID FROM Pages p INNER JOIN MenuLinks ml ON p.PageID = ml.PageID " +
"WHERE ml.MenuID = ?MenuID AND p.SiteID = ?SiteID";
cmd.Parameters.AddWithValue("?MenuID", Convert.ToInt32(BlockData["MenuID"]));
cmd.Parameters.AddWithValue("?SiteID", SIMA_Tools.SiteID);
MenuItems.DataSource = cmd.ExecuteReader();
MenuItems.DataBind();
}
);
}
</code>
När jag anropar den från Page_Load fungerar det fint men när jag försöker anropa den från en postback metod får jag felet:
Invalid attempt to Read when reader is closed.
på raden: MenuItems.DataBind();
Såhär ser min page_load ut:
<code>
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
AddMenuItems();
}
}
</code>
Och metoden som anropas:
<code>
public void DeleteLink(object s, CommandEventArgs e)
{
AddMenuItems();
}
</code>