webForumDet fria alternativet

delete i reader-loop

8 svar · 282 visningar · startad av doggelito

doggelitoMedlem sedan juni 20003 076 inlägg
#1

hur gör man en delete i en loop?

        Dim myConnection As New SqlConnection()
        myConnection.ConnectionString = "user id=xxx;password=3xxx;initial catalog=xxx;data source=xxx;Connect Timeout=10"

        Dim mySelect As New SqlCommand("viewBulkMail", myConnection)
        mySelect.CommandType = CommandType.StoredProcedure
        mySelect.Connection.Open()
        Dim myReader As SqlDataReader = mySelect.ExecuteReader(CommandBehavior.CloseConnection)

        Dim intMsgId As Int32
        Dim strDestination As String
        Dim strSource As String
        Dim strSubject As String
        Dim strBody As String

        Dim mm As New MailMessage()
        While myReader.Read()

            intMsgId = myReader.GetValue(0)
            strDestination = myReader.GetValue(1)
            strSource = myReader.GetValue(2)
            strSubject = myReader.GetValue(3)
            strBody = Replace(myReader.GetValue(4), vbCrLf, "<br>")

            Try
                mm.BodyFormat = MailFormat.Html
                mm.From = strSource
                mm.To = strDestination
                mm.Subject = strSubject
                mm.Body = strBody
                SmtpMail.SmtpServer = "xxx"
                SmtpMail.Send(mm)
                
                'raderar alla skickade meddelanden
                Dim myDelete As New SqlCommand("deleteBulkMail", myConnection)
                myDelete.CommandType = CommandType.StoredProcedure
                myDelete.Parameters.Add("@IDno", SqlDbType.VarChar).Value() = intMsgId
                myDelete.Connection.Open()
                myDelete.ExecuteNonQuery()
                myDelete = Nothing

            Catch
                'uppdaterar status på alla error meddelanden
                Dim myUpdate As New SqlCommand("updateBulkMail", myConnection)
                myUpdate.CommandType = CommandType.StoredProcedure
                myUpdate.Parameters.Add("@IDno", SqlDbType.VarChar).Value() = intMsgId
                myUpdate.Connection.Open()
                myUpdate.ExecuteNonQuery()
                myUpdate = Nothing
            End Try

        End While

felkod:
The connection is already Open (state=Open).
Kommenterar jag bort raden:
myDelete.Connection.Open() så får jag felet:
There is already an open DataReader associated with this Connection which must be closed first.

Ska man använda en annan sorts loop istället för en reader?

GladhMedlem sedan maj 20012 812 inlägg
#2

Om du vill göra som du försöker så måste du öppna en ny connection vid varje delete operation.

Jag skulle dock inte göra så du gör av 2 skäl.

1. Du kommer garanterart får problem med låsningar när du försöker deletea en post i databasen, när en annan connection håller på att läsa den. Typiskt Deadlock problem.

2. Det är extremt obra att göra något som tar lite längre tid i en readerloop. Och detta beror på att du håller din koppling öppen till databasen så länge din reader är öppen.

Så Om du vill göra något som du vill så hade jag löst det genom att hämta all data in i en DataTable eller DataSet och sedan skickat iväg alla mail, och när ett mail skickats så hade jag deletet dens post i databasen. Sedan hade jag även funderat på att implementerar trådning, eftersom det kan ta ett litet tag att koppla sig till smtp-server och skicka iväg mailet, om du då har många mail som skall skickas iväg så kan det vara bra att skicka typ 5 åtgången parallet, istället för att skicka de seriellt som du gör i en loop.

doggelitoMedlem sedan juni 20003 076 inlägg
#3

aha, då är det trådning som gäller eftersom det kommer att kunna vara många mail som skickas!
då ska man bara lista ut hur det funkar! :)
har du nått trådexempel får du gärna posta det! :)

GladhMedlem sedan maj 20012 812 inlägg
#4

Det är lite svårt att förstå bara så där med kodexempel, och eftersom det är olika många mail varje gång, så är det bästa nog att använda .NET's trådpool.

Läs på mer om threads på MSDN eller på någon site, sök på thread C# programming på google och du får massor med träffar. Man måste förstå konceptet med trådhantering, så man gör sin kod threadsafe, alltså att 2 trådar inte kan ändra en och samma variabel samtidigt, detta brukar generera lite lustiga fel, som man oftast inte upptäcker förrens i produktion och som dessutom är jobbiga att hitta, så ett kodexempel hjälper inte så mycket om man inte har all kunskap.

Sedan om det är riktigt mångmail som skall skickas och det är ofta som mailen skall skickas så hade jag lyft ut den funktionallitet från ASP.NET till en WindowsServices som legat och skickat iväg delmängder av mailen med jämnamellan rum, säg att den skickar iväg 100 mail varje kvart, det ger ett jämnare uttnyttjande av prestanda och "sänker" inte maskinen när du vill skicka iväg tusentals mail på en gång.

- M

doggelitoMedlem sedan juni 20003 076 inlägg
#5

Gladh skrev:

lyft ut den funktionallitet från ASP.NET till en WindowsServices

va bra, för det är precis vad det är.
koden är hämtad ifrån min windows service! :)

ang. trådar:
ushh, synd att det är sååå tråkigt att behöva lära sig nya saker hela tiden när man ska göra nånting! :(
men men, det är väl bara att bita i det sura.

GladhMedlem sedan maj 20012 812 inlägg
#6

Gladh skrev:
--------------------------------------------------------------------------------
lyft ut den funktionallitet från ASP.NET till en WindowsServices
--------------------------------------------------------------------------------

va bra, för det är precis vad det är.
koden är hämtad ifrån min windows service!

Bara jag som är knäpp, fick för mig att jag var i ASP.NET forumet. :(

NöffMedlem sedan nov. 2003569 inlägg
#7

och när ett mail skickats så hade jag deletet dens post i databasen

Är det inte bättre att han delete:ar och updaterar allt i en batch när allt är klart istället. Jag tror inte det finns någon begränsning i hur många Querys man kan köra samtidigt i en batch, går säkert att batcha upp ett par tusen delete:s/updates.

string ALL;
for(int u=0;u<DataTable.Rows.Count;u++)
{
try
{
// Gör och skicka mail

ALL+="DELETE FROM konto WITH (TABLOCKX) WHERE kontonr=" + ID + ";";

}catch {

ALL+="UPDATE konto WITH (TABLOCKX) SET error='det gick åt skogen' WHERE ID=" + ID + ";";

}

}

Command.CommandText=ALL;
Command.ExecuteNonQuery();

GladhMedlem sedan maj 20012 812 inlägg
#8

Är det inte bättre att han delete:ar och updaterar allt i en batch när allt är klart istället

Det är mycket möjligt, allt beror på hur viktigt det är att man får korrekt information om vilka mail som har gått iväg och hur viktigt det är att samma mail inte går ut mer än en gång.

Med ett batch jobb så riskerar du att få fler och större problem med "datariktighet" (nytt fint ord jag hittade på) och ju större batch mängden är, destu större problem.

Fast batchjobb är bättre ur prestanda synpunkt även om jag inte skulle löst det så som du har gjort utan istället bara sparat undan ID och kört en SQL sats med en IN() operator i.

doggelitoMedlem sedan juni 20003 076 inlägg
#9

Gladh skrev:

Fast batchjobb är bättre ur prestanda synpunkt även om jag inte skulle löst det så som du har gjort utan istället bara sparat undan ID och kört en SQL sats med en IN() operator i.

Så gör jag faktiskt nu, jag bygger ihop alla id:s till två strängar och skickar in i databasen för radering eller uppdatering. (har inte orkat kika på trådning än!) :)

        While myReader.Read()

            intMsgId = myReader.GetValue(0)
            strDestination = myReader.GetValue(1)
            strSource = myReader.GetValue(2)
            strSubject = myReader.GetValue(3)
            strBody = Replace(myReader.GetValue(4), vbCrLf, "<br>")

            Try
                mm.BodyFormat = MailFormat.Html
                mm.From = strSource
                mm.To = strDestination
                mm.Subject = strSubject
                mm.Body = strBody
                SmtpMail.SmtpServer = "xxx"
                SmtpMail.Send(mm)
                strSentMsgId = strSentMsgId & intMsgId & ","

            Catch
                strRetryMsgId = strRetryMsgId & intMsgId & ","

            End Try

        End While

        'raderar alla skickade meddelanden
        If Len(strSentMsgId) > 0 Then
            Dim myDelete As New SqlCommand("deleteBulkMail", myConnection)
            myDelete.CommandType = CommandType.StoredProcedure
            myDelete.Parameters.Add("@IDno", SqlDbType.VarChar).Value() = strSentMsgId
            myDelete.Connection.Open()
            myDelete.ExecuteNonQuery()
            myDelete = Nothing
        End If

        'uppdaterar alla error meddelanden
        If Len(strRetryMsgId) > 0 Then
            Dim myUpdate As New SqlCommand("updateBulkMail", myConnection)
            myUpdate.CommandType = CommandType.StoredProcedure
            myUpdate.Parameters.Add("@IDno", SqlDbType.VarChar).Value() = strRetryMsgId
            myUpdate.Connection.Open()
            myUpdate.ExecuteNonQuery()
            myUpdate = Nothing
        End If
135 ms totalt · 3 externa anrop · v20260731065814-full.2b84b982
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
130 ms — hämta tråd, inlägg och bilagor (db)