DotNetCampania
Il primo portale campano dedicato allo sviluppo software con tecnologie Microsoft

storia di un deploy 2a parte (questa volta gli aiuti sono ben accetti)

rated by 0 users
This post has 4 Replies | 2 Followers

Top 10 Partecipanti
Maschio
Post 87
Punteggio 1.295
Nezumi Posted: 1 lug 2009 19:31

e giunse il fatidico giorno della consegna,
il cliente entusiasta apprezza la semplicità di utilizzo del software,
a momenti mi lusinga sta cosa, anzi no...mi lusinga...in genere i clienti
dicono cose del tipo "questo non mi piace" "questo nemmeno"...invece
io ne ho beccato uno di quelli tranquilli...poi mi fa...bella la demo
poi la versione completa me la installate sul server ??così
possiamo accederci da tutte le postazioni?...ed io come in un flashback...vedo
passarmi tutte le migliaia di righe di codice che ho scritto per portargli
un'applicazione desktop con db in locale, e lui mi dice (cosa che nessuno dell'area sviluppo sapeva)
che ne vuole una distribuita...ed il panico scese sui miei neuroni
inizio a pensare di dover esporre un servizio sulla macchina server,
poi ci penso e mi dico "va bè ma il servizio lo espone sql server express"
io devo solo fare una stringa di connessione che punti al server
poi ci penso e mi torna in mente una cosa importante "la concorrenza"
e mi ritrovo qui a chiedervi (che cavolo ci hai messo mezz'ora per fare sta domanda!!)

avendo utilizzato i dataset tipizzati con i table adapter in maniera del tutto trasparente
come faccio a gestire la concorrenza???

  • Inserito sotto:
  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 383
Punteggio 5.595

Purtroppo, come immagino tu sappia data la tua disperazione, le applicazini vanno pensate per essere utilizzate in concorrenza, è un requisito quello che ti hanno dato e la cosa migliore in certi casi ancora embrionali e rifare tutto...ma nessuno ne ha il coraggio. Quindi ecco un po' di consigli:

1. Sicuramete, visto che ti preoccupi della cosa, sai benissimo che i dataset tipizzati non supportano l'aggiornamento di più tabelle in modalità transazionale, questo perchè molto semplicemente il pattern table module ragiona sulle singole tabelle; la tua fortuna però è che i TableAdapter sono classi autogenerate che al loro interno usano ado.net, che come ben sai supporta pienamente le transazioni grazie alla classe Transaction: peccato che il table adpater non espongala connection da cui poter richiedere un oggetto di questo tipo. Le soluzioni possibili sono due:

1.a) i table adapter non hanno una classe base, quindi niente ereditarietà, però sono classi partial, quindi puoi rendere pubblica la connectione da quella generare l'oggetto transaction su cui invocare i classici metodi Commit e Roolback quando ti pare. Quindi, crea una connection, genera la transaction e setta la connection, mediante la property che hai creato nelle classi partial, a tutti i table adapter interessati (dato che la transaction è legata alla connection deve essere la stessa per le tabelle che vuoi gestire in modo transazionale)

Dim

 

conn As New SqlClient.SqlConnection(connectionString)

 

Call conn.Open()

 

Dim transaction As SqlClient.SqlTransaction = conn.BeginTransaction()

testata.Connection = conn

dettaglio.Connection = conn

....

2.a) con un po' di sana reflection, a leggero scapito delle prestazioni, puoi semplicemente fare un metodo che si occupi della cosa

 

Public Shared Function avviaTransazione(ByRef tableAdapters() As Object, ByVal connectionString As String) As SqlClient.SqlTransaction

 

Try

 

'Crea e apre la connessione e avvia la transazione

 

Dim conn As New SqlClient.SqlConnection(connectionString)

 

Call conn.Open()

 

Dim transaction As SqlClient.SqlTransaction = conn.BeginTransaction()

 

'Per ogni Table Adapter...

 

Dim tableAdapter As Object

 

For Each tableAdapter In tableAdapters

 

'Recupera il tipo del table adapter e setta la connessione e la transazione

 

Dim tableAdapterType As Type = tableAdapter.GetType()

tableAdapterType.GetProperty(

"Connection", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic).SetValue(tableAdapter, conn, Nothing)

 

Dim adapter As SqlClient.SqlDataAdapter = tableAdapterType.GetProperty("Adapter", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic).GetValue(tableAdapter, Nothing)

adapter.InsertCommand.Transaction = transaction

adapter.UpdateCommand.Transaction = transaction

adapter.DeleteCommand.Transaction = transaction

 

'setta la transazione delle command collection

 

Call tableAdapterType.GetProperty("InitCommandCollection", Reflection.BindingFlags.InvokeMethod Or Reflection.BindingFlags.NonPublic)

 

Dim commandCollection() As SqlClient.SqlCommand = tableAdapterType.GetProperty("CommandCollection", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic).GetValue(tableAdapter, Nothing)

 

Dim connCollection As SqlClient.SqlCommand

 

For Each connCollection In commandCollection

connCollection.Transaction = transaction

 

Next

 

Next

 

 

'Restituisce la transazione

 

Return transaction

 

Catch ex As Exception

 

Call MsgBox(ex.Message, MsgBoxStyle.Critical, "FunzioniComuni->avviaTransazione()")

 

Return Nothing

 

End Try

 

End Function

2. Se hai usato campi identity come chiave in testata che usi come chiave esterna nel dettaglio...hai fatto una pessima scelta...questo perchè i table adapter basano l'identity sui dati correntemente caricati invece che sui dati presenti nel db al momento del salvataggio il che in concorrenza potrebbe creare la spiacevole situazione di accodare il tuo dettaglio a una testata creata in contemporanea... Anche qui puoi risolvere in due modi:

2.a) Elimina il campo identity e gestisci la chiave a manina (che trall'altro nel caso della fatturazione è anche l'unico modo visto che il numero della fattura si resetta ogni anno, è quindi sono chiave numero e data o numero e anno)

2.b) lavora nella transaction, salva la testata, recupera l'id generato e usalo per il dettaglio

Spero ti sia stato utile, se hai ancora problemi puoi mandarmi il tuo codice, gli do volentieri un'occhiata.

Per la tua gioia il codice è in VB, così te lo devi tradurre in C# (un po' di esercizio non fa mai male...)

A presto.

transaction.Commit()

  • | Punteggio Post: 5
Top 10 Partecipanti
Maschio
Post 383
Punteggio 5.595

Rettifico, la connection è già pubblica, quindi ancora più semplice...

  • | Punteggio Post: 5
Top 10 Partecipanti
Maschio
Post 383
Punteggio 5.595

Abbiamo scoperto che in visual studio 2008 è stato introdotto il TableAdapterManager, che risove la maggior parte dei problemmi discussi. A breve il nostro Nezumi, che lo sta testando, ci illuminerà!!!!

  • | Punteggio Post: 20
Top 10 Partecipanti
Maschio
Post 87
Punteggio 1.295

Citando MSDN

TableAdapterManager è un nuovo componente di Visual Studio 2008 che si basa su funzionalità dei dati esistenti, quali dataset tipizzati e TableAdapter, e fornisce la funzionalità per salvare i dati nelle tabelle dati correlate. TableAdapterManager utilizza le relazioni di chiavi esterne relative alle tabelle dati per determinare l'ordine corretto di invio dei comandi di inserimento, aggiornamento ed eliminazione da un dataset al database senza violare i vincoli di chiave esterna (integrità referenziale) nel database.

Metodo UpdateAll

Salva tutti i dati presenti in tutte le tabelle dati.

 

Proprietà BackUpDataSetBeforeUpdate

Boolean. Determina se creare una copia di backup del dataset prima di eseguire il metodo TableAdapterManager.UpdateAll.

 

Nometabella Proprietà TableAdapter

Rappresenta un TableAdapter. Il TableAdapterManager generato contiene una proprietà per ogni TableAdapter gestito. Ad esempio, un dataset con una tabella Customers e Orders viene generato con un TableAdapterManager contenente le proprietà CustomersTableAdapter e OrdersTableAdapter.

Proprietà UpdateOrder

Controlla l'ordine di esecuzione dei singoli comandi di inserimento, aggiornamento ed eliminazione. Impostare questa proprietà su uno dei valori dell'enumerazione TableAdapterManager.UpdateOrderOption.

Per impostazione predefinita, la proprietà UpdateOrder viene impostata su InsertUpdateDelete. Ciò significa che i comandi di inserimento, aggiornamento ed eliminazione vengono eseguiti per tutte le tabelle del dataset esattamente in questo ordine. Per ulteriori informazioni, vedere Procedura: impostare l'ordine per l'esecuzione di un aggiornamento gerarchico.


in pratica supponendo di avere il dettaglio di un entità e di una a cui si relazione

basterà sulla chiusura di un form (o in qualsiasi altra posizione decidiate di salvare)

scrivere:

entita1BindingSource.EndEdit();
entità2BindingSource.EndEdit();
tableAdapterManager.UpdateAll(dataset);

niente di più facile credo... no??? alla prox

  • | Punteggio Post: 5
Pagina 1 di 1 (5 elementi) | RSS

Associazione Culturale DotNetCampania - C.F.: 95127870632

Powered by Community Server (Commercial Edition), by Telligent Systems