Autor Beitrag
danielf
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Do 15.04.10 10:28 
Hallo,

ich möchte den Inhalt einer Xml-Datei in eine Datenbank speichern. Mir ist klar, das dies nur bis zu einem gewissen Rahmen möglich ist.Ich habe zuerst ein XmlSchema erstellt und ein passendes Xml File. Die DataSet-Struktur (Tabellen etc.) sieht sehr gut aus. Ich kann auch das Xml in das DataSet bei Berücksichtigung des Schemas laden:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
DataSet ds = new DataSet();

// Read schema
using (StreamReader schemaStream = new StreamReader(path))
{
   ds.ReadXmlSchema(schemaStream);

   ds.ReadXml(tempFilename);
}


Nun möchte ich dieses DataSet in eine Datenbank schreiben. Aber wie?

Danke und Gruß

Daniel

Edit: Ich will/kann nicht in Vorhinein die Struktur der Datenbank festlegen. D.h. ich bekomme eine Xml-Datei und ein dazugehöriges Schema und will daraus a) die notwendigen Tabellen und Relationen erstellen und b) den Inhalt der Xml-Datei ablegen.
JüTho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2021
Erhaltene Danke: 6

Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
BeitragVerfasst: Do 15.04.10 13:53 
Hallo Daniel,

mit den Bordmitteln von ADO.NET geht das nicht. Du kannst dir nur aus dem DataSet (oder auch direkt aus der xml-Datei) einen CREATE TABLE-Befehl zusammenstellen und diesen per DbCommand.ExecuteNonQuery an die DB schicken.

Mit einem O/R-Mapper oder Linq2Sql könnte es so etwas geben. Vor allem wenn diese Aufgabe häufiger zu erledigen ist (mit unterschiedlichen Dateien), wäre das wohl vorzuziehen.

Gruß Jürgen
danielf Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Do 15.04.10 14:19 
Danke für die Antwort.

Also das Problem ist, dass ich noch nicht weiß wie die Datenstruktur aussehen wird, weil immer wieder neue hinzu kommen (meine Server Anwendung bekommt als Ergebnis eben eine Xml-Datei mit Schema). Zuerst wollte ich es generisch speichern in einer Key-Value-Beziehung mit Metadaten. Da diese aber untypisiert wären und daher einen hohen Aufwand bei der weiteren Verarbeitung verursachen dachte ich mir ich kann mithilfe eines Schemas die entsprechende Datenbank-Struktur erstellen und die ankommenden Xml-Dateien darüber ablegen. Des Weiterem gehe ich von dem Ansatz aus, dass DB -> DataSet -> DataGrid -> DataSet -> Db funktioniert und dann auch Xml -> DataSet -> Db gehen sollte.

Für ein O/R-Mapping fehlt mir ja das Objekt ? :/

Bei Linq2Sql komm ich auf die Idee Linq2Xsd... vlt. hilft mir das weiter, wie ich die Daten in die DB bekomme..

Oder wie soll das konkret mit O/R-Mapper bzw. Linq2Sql funktionieren?

Die Umsetzung sehe ich nicht ...
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Do 15.04.10 16:49 
Hab für dich mal zwei Lösungen für den SQL Server erguggelt ;) : social.msdn.microsof...11e220048c3/#1003715
Wenn du allerdings lieber auf bewährten, DB-Server-unabhängigen Code setzen willst, solltest du dir mal NHibernates SchemaExport-Klasse anschauen. Linq2SQL und EF bieten so etwas AFAIK nicht.

PS: Gilt Linq2SQL jetzt nicht mehr als O/RM :< ?

_________________
>λ=
danielf Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Fr 16.04.10 13:18 
Okay, mithilfe von SMO (Microsoft.SqlServer.Management.Smo) und dem Artikel Copy Data from a DataTable to a SQLServer Database using SQLServer Management Objects and SqlBulkCopy habe ich es hin bekommen, dass ich von einem Xml Schema (XDS) die Struktur in einer Datenbank erstellen kann. Im nächsten Schritt wird dann die passende Xml-Datei eingelesen und Tabelle für Tabelle in die Datenbank kopiert.

Als nächstes wird es interessant werden, ob ich die Beziehungen abgebildet bekomme - ich melde mich wieder ;) (falls es jemanden interessiert).

ausblenden volle Höhe C#-Quelltext
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:
            SqlConnection sc = new SqlConnection(
                @"Data Source=.\SQLEXPRESS;" +
                @"Integrated Security=True;");
            sc.Open();

            Server server = new Server(new ServerConnection(sc));
            Database db = server.Databases["Test"];

            if (db != null)
            {
                db.Drop();
                db = null;
            }

            if (db == null)
            {
                db = new Database(server, "Test");
            }

            db.Create();

            DataSet ds = new DataSet();

            ds.ReadXmlSchema(@"C:\result.xsd");
            ds.ReadXml(@"C:\clean_result.xml");

            foreach (DataTable table in ds.Tables)
            {
                Table testTable = new Table(db, table.TableName);

                foreach (DataColumn column in table.Columns)
                {
                    Column c = new Column(testTable, column.ColumnName);

                    c.DataType = DataSetHelper.GetSqlServerDataType(DataSetHelper.GetSqlDBTypeFromType(column.DataType), int.MaxValue, 4);
                    c.Nullable = column.AllowDBNull;
                    c.Identity = column.Unique;
                    testTable.Columns.Add(c);
                }

                testTable.Create();
            }

            SqlBulkCopy bulkCopy = new SqlBulkCopy(sc);

            foreach (DataTable sourceTable in ds.Tables)
            {
                bulkCopy.DestinationTableName = sourceTable.TableName;
                bulkCopy.WriteToServer(sourceTable);
            }
danielf Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Di 20.04.10 11:02 
So, wie angekündigt stelle ich noch meine Lösung für das erstellen einer Datenbankstruktur aus einem Dataset:

Struktur erstellen:
ausblenden volle Höhe C#-Quelltext
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:
       private static void CreateDatabaseStructur(Database database, DataSet structur)
        {
            foreach (DataTable table in structur.Tables)
            {
                Table testTable = new Table(database, table.TableName);

                // add columns to new table
                foreach (DataColumn column in table.Columns)
                {
                    Column c = new Column(testTable, column.ColumnName);

                    c.DataType = DataSetHelper.GetSqlServerDataType(DataSetHelper.GetSqlDBTypeFromType(column.DataType), int.MaxValue, 4);
                    c.Nullable = column.AllowDBNull;


                    testTable.Columns.Add(c);
                }

                testTable.Create();

                // add primary keys
                foreach (var primaryKey in table.PrimaryKey)
                {
                    Index index = new Index(testTable, primaryKey.ColumnName);
                    index.IndexKeyType = IndexKeyType.DriPrimaryKey;
                    index.IndexedColumns.Add(new IndexedColumn(index, primaryKey.ColumnName));

                    testTable.Indexes.Add(index);
                    index.Create();
                }

                // add foreign keys
                foreach (DataRelation relation in table.ParentRelations)
                {
                    ForeignKey fk = new ForeignKey(testTable, relation.RelationName);

                    for (int i = 0; i < relation.ChildColumns.Length; i++)
                    {
                        fk.Columns.Add(new ForeignKeyColumn(
                            fk,
                            relation.ChildColumns[i].ColumnName,
                            relation.ParentColumns[i].ColumnName));
                    }

                    fk.ReferencedTable = relation.ParentTable.TableName;
                    fk.Create();
                }
            }
        }


DataSet in SqlConnection kopieren:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
        private static void FillDatabase(SqlConnection sqlConnection, DataSet pluginResult)
        {
            SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConnection);

            foreach (DataTable sourceTable in pluginResult.Tables)
            {
                bulkCopy.DestinationTableName = sourceTable.TableName;
                bulkCopy.WriteToServer(sourceTable);
            }
        }


Falls jemand Interesse an der DataSetHelper-Klases hat soll er Bescheid sagen. Die ist eine Erweiterung aus dem Vorherigen Artiekl "Copy Data from a DataTable to a SQLServer Database using SQLServer Management Objects and SqlBulkCopy".

Gruß
danielf Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Do 22.04.10 17:03 
Ich muss den Thread leider nochmal aufmachen, weil nochmal Probleme aufgetaucht sind.

Das erstmalige erstellen der Datenbankstruktur (wenn man die FK erst erstellt, wenn alle Tabellen existieren) funktioniert einwandfrei. Auch das Einlesen einer Xml-Datei in ein DataSet und kopieren der Daten des DataSet in die Datenbank funktioniert.

Was mir nun Probleme macht ist folgendes:

Nachdem ich die DB-Struktur erstellt habe und eine Xml-Datei in die DB-Struktur kopiere kann ich keine weiteren Xml-Dateien in die DB kopieren. Das liegt daran, dass jede Xml-Datei die ich ins DataSet einlese die PrimaryKeys von vornen beginnen und ich dann beim Kopieren Probleme bekomme.

Wie bekomme ich eine weitere Xml-Datei zur Db hinzugefügt?
JüTho
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2021
Erhaltene Danke: 6

Win XP Prof
C# 2.0 (#D für NET 2.0, dazu Firebird); früher Delphi 5 und Delphi 2005 Pro
BeitragVerfasst: Do 22.04.10 17:46 
Hallo Daniel,

ich habe keine Idee, wie das bei deinen Xml-Dateien und den benötigten FKs am sinnvollsten zu erledigen ist. Gängige Verfahren sind: (a) die IDs in den Daten auf null oder -1 setzen, sodass die IDs beim Insert von der DB vergeben werden; (b) die letzte vergebene ID von der DB holen - ich glaube, das geht bei MS-SQL mit SELECT IDENTITY o.ä. - und diese als Startwert für die betreffende DataTable verwenden.

Gruß Jürgen
danielf Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Do 22.04.10 21:04 
Danke für die Antwort.

Ich werde Variante a) versuchen. Wenn diese nicht funktioniert werde ich eine gecachte ID Nummer (Variante b) nehmen. Wenn es funktioniert hat, werde ich den Status wieder ändern).
danielf Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Mo 26.04.10 07:59 
Okay, das funktioniert leider Beides nicht.

Das Problem dabei ist, dass die "Primary IDs" von dem DataSet gesetzt werden. Es ist kein tatsächliches Feld sondern repräsentiert die Beziehung der einzelnen Tabellen innerhalb des DataSet und diese kann ich meines Erachtens nicht ändern :(

Vlt./Hoffentlich sehe ich es ja falsch, ich benötige wohl noch ein paar Tipps :/
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 26.04.10 11:18 
user profile icondanielf hat folgendes geschrieben Zum zitierten Posting springen:
Es ist kein tatsächliches Feld sondern repräsentiert die Beziehung der einzelnen Tabellen innerhalb des DataSet und diese kann ich meines Erachtens nicht ändern :(
Aber es muss doch in deiner Tabelle eine wirkliche PK-Spalte geben? hier funktioniert das jedenfalls ;) .

_________________
>λ=
danielf Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Mo 26.04.10 12:57 
Es gibt eine, aber diese will ich ja nicht alle selber neu setzen - dann könnte ich ja gleich die Werte selber in die DB schreiben.

Zusammenfassung:
Ich habe eine Xml-Datei (und ein dazugehöriges Schema). Diese lese ich in eine DataSet ein, welches mir eine Tabelle Struktur erstellt. Diese kann ich auf ein RD abbilden und auch das DataSet darin speichern.

Problem:
Will ich eine weitere XmlDatei in der DB speichern habe ich das Problem das die PrimaryKeys gleich sind. Das DataSet verwende diese um die Beziehung der Xml-Hierarchie abzubilden.

Bsp:
ausblenden XML-Daten
1:
2:
3:
4:
<root>
  <child/>
  <child/>
</root>


Tabelle Root:
ID

Tabelle Child:
RootId
ID

Das legt das DataSet an. Es erstelle aber immer eine Root ID = 0; und eine entsprechende ID des childs = 0. Würde ich nun Root.ID selber setzen, müsste ich bei Child.RootId auch editieren usw. Dann habe ich den konform verloren und könnte gleich ein Insert-Statement verwenden.

Ich hoffe ich konnte das Problem nochmal verdeutlichen und warum es nicht genügt einfach die PK nicht zu setzen (weil die Xml-Hierarchie).

Ich teste gerade diverse Vorgehensweisen hinsichtlich Datentyp Xml in der MS Sql Server.
danielf Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Di 27.04.10 14:30 
Okay, den DataType Xml in der Datenbank habe ich verworfen. Das bringt nur interne Performance gegenüber untyped Xml (ohne Schema). Es bietet sich wohl eher an, wenn man wirklich unstrukturierte XmlDaten hat und später direkt in der Datenbank Xml Queries (XPath/XQuery) absetzen will.

Ich habe das importieren nun folgendermaßen lösen können:

1) Die Datenbankstruktur anhand eines Xml Schema erstellen (siehe vorheriger Code)

2) DataSet erstellen und Xml Schema laden (ReadXmlSchema)

3) Jede Tabelle des DataSet aus der Datenbank mit Informationen füllen (DataAdapter.Fill(dataset.table));

4) Neue Xml Datei einlesen DataSet.ReadXml(stream);

5) Daten zurück in die Datenbank schreiben. Dies geschieht wie beim auslesen für jede Tablle (DataAdapter.update(dataset, tabelle)

So funktioniert es auch mit der Nachbarin ;)

Gruß

PS: Falls jemand auch über dieses "Problem" stolpert und er noch Fragen hat stelle ich ihm meine Vorgehensweise gerne detaillierter vor und falls ich hier Bockmist baue und es jemand ließt/merkt bin ich demjenigen sehr dankbar wenn er mich daraufhin hinweist.