[C#] DataContractSerializer op een DataSet

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • NetWave
  • Registratie: Oktober 2008
  • Laatst online: 17-03 16:45
Ik heb een probleem met het via WCF doorgeven van een typed dataset. Ik heb het probleem zo bondig mogelijk samengevat; in realiteit is de code anders.

Ik heb een dataset met (onder meer) 2 tabellen, "Master" en "Detail". Beide worden gelezen van de database.

Tussen de twee tabellen ligt een relatie, zodat het verwijderen van records in de "Master" tabel ook records uit de "Detail" tabel zal schrappen.

Als de gebruiker z'n wijzigingen gaat bewaren, dan worden beide tabellen verwijderd en opnieuw ingevuld. In de database moet de oude data dus geschrapt worden, en de nieuwe data toegevoegd.

Daarna wordt de dataset via WCF naar de database server gestuurd om de data effectief te updaten. De DataContractSerializer loopt echter niet 100%.

Goed, het voorbeeldprogramma (Tot lijn 39 is het setup van de demo dataset) :

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Runtime.Serialization;
using System.IO;

namespace DataSetProblem
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create dummy dataset
            
            DataTable Master = new DataTable("Master");
            DataTable Detail = new DataTable("Detail");

            Master.Columns.Add("MasterID", typeof(int));
            Master.Columns.Add("Description", typeof(string));

            Detail.Columns.Add("MasterID", typeof(int));
            Detail.Columns.Add("DetailID", typeof(int));
            Detail.Columns.Add("Description", typeof(string));

            DataSet DemoDataSet = new DataSet("DemoDataSet");

            DemoDataSet.Tables.Add(Master);
            DemoDataSet.Tables.Add(Detail);

            DemoDataSet.Relations.Add(new DataRelation("MasterDetail", Master.Columns["MasterID"], Detail.Columns["MasterID"]));

            // Fill with dummy data

            Master.Rows.Add(1, "Master line 1");
            Detail.Rows.Add(1, 1, "Detail line 1");

            DemoDataSet.AcceptChanges();

            // Delete the data

            Master.Rows[0].Delete();
    
            Master.Rows.Add(1, "New master line 1");
            Detail.Rows.Add(1, 1, "New detail line 1");

            // Serialize

            DumpDataSet(DemoDataSet);

            Console.WriteLine("Serializing...");

            DataContractSerializer serializer = new DataContractSerializer(DemoDataSet.GetType());
    
            using (Stream s = File.Create(@"c:\DataSetDump.xml"))
            {
                serializer.WriteObject(s, DemoDataSet);
            }

            // Deserialize

            Console.WriteLine("Deserializing...");

            DataSet DemoDataSetDeserialized;

            using (Stream s = File.OpenRead(@"c:\DataSetDump.xml"))
            {
                DemoDataSetDeserialized = (DataSet) serializer.ReadObject(s);
            }

            DumpDataSet(DemoDataSetDeserialized);

            Console.ReadLine();
        }

        public static void DumpDataSet(DataSet ds)
        {
            foreach (DataTable Table in ds.Tables)
            {
                int Total   = Table.Rows.Count;
                int Deleted = Table.Select(null, null, DataViewRowState.Deleted).Length;
                int Added   = Table.Select(null, null, DataViewRowState.Added).Length;

                Console.WriteLine("Table '{0}' : {1} total rows ({2} deleted, {3} added)", Table.TableName, Total, Deleted, Added);
            }
        }
    }
}


het resultaat hiervan is:

code:
1
2
3
4
5
6
Table 'Master' : 2 total rows (1 deleted, 1 added)
Table 'Detail' : 2 total rows (1 deleted, 1 added)
Serializing...
Deserializing...
Table 'Master' : 2 total rows (1 deleted, 1 added)
Table 'Detail' : 1 total rows (1 deleted, 0 added)


je ziet dat er dus een verschil is tussen beide datasets.

Mijn toegevoegd record van "Detail" is verdwenen!

het DiffGram van "DataSetDump.xml" ziet er als volgt uit:

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<diffgr:diffgram xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <DemoDataSet>
        <Master diffgr:id="Master2" msdata:rowOrder="1" diffgr:hasChanges="inserted">
            <MasterID>1</MasterID> 
            <Description>New master line 1</Description> 
        </Master>
        <Detail diffgr:id="Detail2" msdata:rowOrder="1" diffgr:hasChanges="inserted">
            <MasterID>1</MasterID> 
            <DetailID>1</DetailID> 
            <Description>New detail line 1</Description> 
        </Detail>
    </DemoDataSet>
    <diffgr:before>
        <Master diffgr:id="Master1" msdata:rowOrder="0">
            <MasterID>1</MasterID> 
            <Description>Master line 1</Description> 
        </Master>
        <Detail diffgr:id="Detail1" msdata:rowOrder="0">
            <MasterID>1</MasterID> 
            <DetailID>1</DetailID> 
            <Description>Detail line 1</Description> 
        </Detail>
    </diffgr:before>
</diffgr:diffgram>


Deze is dus wél correct, en toch loopt het deserializen mis.

Mijn idee is: DataContractSerializer.Deserialize() voegt eerst de "inserted" records toe. Daarna schrapt hij het gedelete record in "Master" waardoor hij, door de relatie, ook het pas toegevoegde record van de "Detail" schrapt, wat niet de bedoeling is.

Kern van het probleem is dus volgens mij dat ik bij het toevoegen van nieuwe records opnieuw dezelfde keys van de geschrapte records gebruik. Maar dat kan/mag ik niet veranderen (moet steeds met 1 beginnen).

Is heb er echter géén idee van hoe ik dit kan oplossen. Iemand?

Acties:
  • 0 Henk 'm!

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Hergebruik van primary keys is per definitie al een bad practice. En een DataSet is (helaas) nou niet echt ontworpen om transaction based te zijn. Het houd de wijzigingen als totaal bij. Niet de afzonderlijke commands waardoor een 'replay' van de wijzigigen niet mogelijk is..

Daarnaast is de DataContractSerializer niet de default dataset serializer. Gaat het wel goed (of beter) als je DemoSet.WriteXml(@"c:\DataSetDump.xml", XmlWriteMode.DiffGram) wegschrijft er daarna weer inleest met ReadXML(@"c:\DataSetDump.xml", XmlReadMode.DiffGram)?

If it isn't broken, fix it until it is..


Acties:
  • 0 Henk 'm!

  • NetWave
  • Registratie: Oktober 2008
  • Laatst online: 17-03 16:45
Dat van die keys klopt wel, maar da's legacy van een vorige database (don't ask).

Je argumenten tegen het DataSet op zich, begrijp ik niet goed. Je kan toch opvragen wat-ie naar de database gaat sturen? En met een klein stukje code in de partial class van het typed dataset werkt het prima met transacties hoor.

Voor WCF is de DataContractSerializer wel de default serializer. Maar eigenlijk doet dat niet ter zake, ook een BinaryFormatter geeft hetzelfde probleem. Volgens mij omdat een DataSet intern altijd z'n DiffGram (dus XML) zal serializen, in welk formaat dan ook.

DemoDataSet.Clear();
DemoDataSet.ReadXML(@"c:\DataSetDump.xml", XmlReadMode.DiffGram);

...geeft overigens net hetzelfde resultaat.

bedankt voor het meedenken!

Acties:
  • 0 Henk 'm!

  • D-Raven
  • Registratie: November 2001
  • Laatst online: 10-09 20:32
Je zou het wellicht eens kunnen proberen met de NetDataContractSerializer. Deze houdt expliciet rekening met .Net data types.
Nadeel is dat je dan wat compatibiliteit kwijtraakt met je WCF service maar als je al DataSets aan het rondgooien bent dan ben je dat sowieso al kwijt....

Deze link verwijst naar een artikel erover.

En misschien dat je dit al weet maar had kan je iig een hoop tijd schelen.

Acties:
  • 0 Henk 'm!

  • NetWave
  • Registratie: Oktober 2008
  • Laatst online: 17-03 16:45
Het enige verschil tussen NetDataContractSerializer en DataConstractSerializer is dat hij het het type en de assembly van "DemoDataSet" bij de XML zet.

Het DiffGram is echter identiek, en dus blijft ook het probleem bestaan.

Je tweede link had je enkele weken vroeger moeten doorsturen, ik ben er pas hardhandig achter gekomen 8)7

Leergeld, heet zoiets dan...