[ hninel @ 06.06.2005. 21:24 ] @
Interesuje me kako da ucinim update datagrida nakon snimanja u bazu (dataadapter.update(ds.getchanges()).

Desava se slijdece:

Kada unesem novi red on poveca autoincrement za 1 (redni broj) i to lijepo snimim sa dadapter.Update(...), nakon toga ga recimo izbrisem i sve opet lijepo snimim. Ugasim formu i ponovo je podignem i unesem novi red... Eh tu nastaju problemi...

Kada unesem novi rekord (red), sve je OK, on ga inkrementuje za 1, no kada ga snimi sa daadapter.Update(...) u DataGridu je i dalje onaj broj inkrementovani, te ako sad pokusam da ga obrisem, javlja gresku.

Znaci u ovom trenutku mi trebaju podaci iz baze, jer je ovaj autoincrement broj u bazi drugaciji. Kako da izvrsim update iz baze tj. neki refresh...

Hvala.
[ i_nenad @ 07.06.2005. 09:27 ] @
Hmm, mislim da ce ti ovo resiti problem :

Code:

Dim _ds as dataset = objDS.GetChanges
DA.Update(_ds,"ImeDataTable")
objDS.Merge(_ds)

Posle merge imas u objDS-u nove vrednosti iz baze.

Pozdrav
[ mmix @ 07.06.2005. 12:06 ] @
Nije to resenje,

DataAdapter ima jednu lepu osobinu da ako stored procedura za insert ili update vrati recordset, DA pretpostavlja da je prvi red tog recordseta unesena tj. izmenjena vrednost reda i to ce naterati DA da promeni vredosti u redu DS-a.

Ovako npr. izgleda stored procedure koja radi po tom principu (ako imas insert trigger nad tabelom, onda iskoristi SCOPE_IDENTITY() umesto @@IDENTITY):

Code:

ALTER PROCEDURE dbo.sp_ImportBatch_Insert
(
    @UserId int,
    @CreatedOn datetime
)
AS
BEGIN
    SET NOCOUNT OFF;
    INSERT INTO ImportBatch(UserId, CreatedOn) VALUES (@UserId, @CreatedOn);
    SELECT ImportBatchId, UserId, CreatedOn FROM ImportBatch WHERE (ImportBatchId = @@IDENTITY);
END


E sad mala "kuvarska" tehnika: U dataset shemi te tabele, postavi Seed i Increment na -1 za identity polje. Ovo je veoma vazno zbog konkurentnosti, ako se vec napuni dataset i onda doda novi red on nece imati vrednost (max(ID)+1) kako se ocekuje (i kako MSSQL radi), nego 1, sto ce ti izazvati key-violation u datasetu ako vec postoji red sa ID=1. Ako su ti Seed i Increment -1, onda ce novi redovi dobijati ID-ove redom -1, -2, etc. i nikad se nece "pobiti" sa realnim IDovima. U trnutku kad Updatujes dataset, gornja stored procedura ce tim redovima dodeliti realne slobodne IDove.
[ i_nenad @ 07.06.2005. 12:49 ] @
Covek je lepo napisao sintaxu , koju koristi:
dataadapter.update(ds.getchanges())
predpostavljam da koristi generisane comande dataadaptera, a ne stored procedure.
sam po sebi adapter pri komandi prenosi ds ByRef i automatski ga updatuje, ali u ovom slucaju je konkretan problem to sto se updateje ds.Getchanges, sto je novi ds.
znaci jedno resenje je :
dataadapter.update(ds), ako broj slogova koje imas u tebeli nije veliki ili , merge koji sam dao u prvom reply-ju.
Pozdrav
[ mmix @ 07.06.2005. 14:36 ] @
Citat:
i_nenad: Covek je lepo napisao sintaxu , koju koristi:
dataadapter.update(ds.getchanges())
predpostavljam da koristi generisane comande dataadaptera, a ne stored procedure.
...
znaci jedno resenje je :
dataadapter.update(ds), ako broj slogova koje imas u tebeli nije veliki ili , merge koji sam dao u prvom reply-ju.
Pozdrav


Hmm, ajd da probam da obrazlozim zasto ovo ne valja.

1. Cak i kad se automatski generisu komande kao sto pretpostavljas, i dalje je po defaultu ukljucena opcija "Refresh dataset", koja ce napraviti SQL komandu sa sledecim CommandText-om:
Code:

UPDATE ImportBatch SET UserId = @UserId, CreatedOn = @CreatedOn WHERE (ImportBatchId = @Original_ImportBatchId); 
SELECT ImportBatchId, UserId, CreatedOn FROM ImportBatch WHERE (ImportBatchId = @ImportBatchId)

sto se svodi na stored proceduru koju sam napisao gore. Ako nema SELECT u komandi, ne postoji automaski nacin da se osvezi dataset sa generisanim vrednostima. To je prvo sto mora da se proveri, bez toga je ostatak ovog topica nevazan.

2. ds.GetChanges proizvede kopije redova koji su dodati/promenjeni/obrisani. Ti redovi vise nemaju nikakve veze sa originalnim redovima. Kad se ovaj dataset posalje adapteru ti "novi" redovi dobiju nove IDove ili ostanu isti (ako nema SELECT dela iz tacke 1.), nazovimo ovaj dataset DSCH

3. Kad radis Merge, jedini nacin da dataset zakljuci koji redovi su novi a koji izmenjeni je PrimaryKey. I sad se javljaju dve opcije (od kojih nijedna ne valja):

3.1 (bez SELECTA u komandi): ID polje je ostalo isto, dataset ce ostale vredosti u redu zameniti sa onima iz DSCH, tj, nece uraditi nista posto dataadapter nije menjao DSCH jer se iz komande/stored procedure nije nista vratilo.
3.2 (sa SELECT): ID polje ce dobiti novu realnu vrednost u DSCH. ds ce pretraziti svoj primary key i videce da ne postoji red sa tim ID-om i zakljucice da je taj red iz DSCH ustvari NOVI RED i dodace ga umesto da stari zameni novim. Kao posledica ovoga svi dodati redovi u ds ce se duplirati samo sa drugim ID-ovima. Dalje, svi redovi koji su bili iskopirani sa GetChanges ce i dalje ostati u statusu "Added" i ako ponovo pozoves GetChanges,Update bice ponovo dodati u bazu.


Bottom line, ne postoji razlog da se koristi dataadapter.update(ds.getchanges()) sem kad se dataset salje preko webservisa ili u remoting pa da se smanji pritisak na bandwitdh (a u tom slucaju posle svakog merge-a mora da se radi ciscenje duplikata).
U WinForms je UVEK bolje koristiti dataadapter.update(ds), posto ce dataadapter pre poziva komandi uraditi pretrazivanje dataset-a isto kao sto ce to uraditi ds.GetChanges() samo sto ce:

a) biti brze, posto nece praviti kopije redova
b) biti efikasnije, posto ce SELECT u komandi automatski promeniti red u originalnom datasetu te nema potrebe za merge-om.

Broj redova nema veze sa ovim, ako ukupno ima milion redova u datasetu, i dataadapter i ds.GetChanges moraju da prodju kroz isti proces da bi odredili koji treba da se posalju bazi (tj koji treba da se iskopiraju u DSCH).

PS. Sad vidim da nisam eksplicitno naveo da uz moj primer mora da se koristi dataadapter.update(ds), izvinjavam se za tu omasku.

PS> Ako mi i dalje ne verujes probaj sledeci kod (dataset je zakacen za poruku):

Code:

    class Class1
    {

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            DatasetX dsx = new DatasetX();
            dsx.tabela.AddtabelaRow("novi red 1");
            dsx.tabela.AddtabelaRow("novi red 2");
            Console.WriteLine("Pre getchanges/update/merge");
            PrintTable(dsx.tabela);
            DatasetX dsch = (DatasetX)dsx.GetChanges();
            SimulateDataAdapter(dsch, 25);
            dsx.Merge(dsch);
            Console.WriteLine("Post merge");
            PrintTable(dsx.tabela);
            dsch = (DatasetX)dsx.GetChanges();
            SimulateDataAdapter(dsch, 35);
            dsx.Merge(dsch);
            Console.WriteLine("Post second merge");
            PrintTable(dsx.tabela);
            Console.ReadLine();
        }

        static void PrintTable(DatasetX.tabelaDataTable tbl)
        {
            foreach (DatasetX.tabelaRow _row in tbl.Rows)
            {
                Console.Write("{0,3} : {1}\n", _row.ID, _row.polje);
            }
        }

        static void SimulateDataAdapter(DatasetX dsch, int start)
        {
            int xx = start; // recimo da je start prvi sledeci slobodni u bazi
            foreach (DatasetX.tabelaRow _row in dsch.tabela.Rows)
            {
                _row.ID = xx++;
            }
            dsch.AcceptChanges();
        }
    }


ovo je izlaz koji ces dobiti:

Code:

Pre getchanges/update/merge
 -1 : novi red 1
 -2 : novi red 2
Post merge
 -1 : novi red 1
 -2 : novi red 2
 25 : novi red 1
 26 : novi red 2
Post second merge
 -1 : novi red 1
 -2 : novi red 2
 25 : novi red 1
 26 : novi red 2
 35 : novi red 1
 36 : novi red 2