Frage

Kennt jemand eine Möglichkeit, Datenbanktabellen für eine bestimmte Klasse automatisch zu generieren?Ich bin nicht auf der Suche nach einer vollständigen Persistenzschicht – ich verwende bereits eine Datenzugriffslösung, aber ich muss plötzlich viele Informationen aus einer großen Anzahl von Klassen speichern und möchte sie wirklich nicht erstellen alle diese Tabellen von Hand.Nehmen wir zum Beispiel die folgende Klasse:

class Foo
{
    private string property1;
    public string Property1
    {
        get { return property1; }
        set { property1 = value; }
    }

    private int property2;
    public int Property2
    {
        get { return property2; }
        set { property2 = value; }
    }
}

Ich würde das folgende SQL erwarten:

CREATE TABLE Foo
(
    Property1 VARCHAR(500),
    Property2 INT
)

Ich frage mich auch, wie Sie mit komplexen Typen umgehen könnten.Wenn wir das beispielsweise in der zuvor zitierten Klasse wie folgt ändern würden:

class Foo
{
    private string property1;
    public string Property1
    {
        get { return property1; }
        set { property1 = value; }
    }

    private System.Management.ManagementObject property2;
    public System.Management.ManagementObject Property2
    {
        get { return property2; }
        set { property2 = value; }
    }
}

Wie könnte ich damit umgehen?

Ich habe versucht, die Datenbankskripte mithilfe von Reflection selbst automatisch zu generieren, um die Eigenschaften jeder Klasse aufzuzählen, aber das ist umständlich und die komplexen Datentypen machen mich ratlos.

War es hilfreich?

Lösung

Es ist wirklich spät und ich habe nur etwa 10 Minuten damit verbracht, also ist es extrem schlampig, aber es funktioniert und wird Ihnen einen guten Ausgangspunkt geben:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace TableGenerator
{
    class Program
    {
        static void Main(string[] args)
        {
            List<TableClass> tables = new List<TableClass>();

            // Pass assembly name via argument
            Assembly a = Assembly.LoadFile(args[0]);

            Type[] types = a.GetTypes();

            // Get Types in the assembly.
            foreach (Type t in types)
            {
                TableClass tc = new TableClass(t);                
                tables.Add(tc);
            }

            // Create SQL for each table
            foreach (TableClass table in tables)
            {
                Console.WriteLine(table.CreateTableScript());
                Console.WriteLine();
            }

            // Total Hacked way to find FK relationships! Too lazy to fix right now
            foreach (TableClass table in tables)
            {
                foreach (KeyValuePair<String, Type> field in table.Fields)
                {
                    foreach (TableClass t2 in tables)
                    {
                        if (field.Value.Name == t2.ClassName)
                        {
                            // We have a FK Relationship!
                            Console.WriteLine("GO");
                            Console.WriteLine("ALTER TABLE " + table.ClassName + " WITH NOCHECK");
                            Console.WriteLine("ADD CONSTRAINT FK_" + field.Key + " FOREIGN KEY (" + field.Key + ") REFERENCES " + t2.ClassName + "(ID)");
                            Console.WriteLine("GO");

                        }
                    }
                }
            }
        }
    }

    public class TableClass
    {
        private List<KeyValuePair<String, Type>> _fieldInfo = new List<KeyValuePair<String, Type>>();
        private string _className = String.Empty;

        private Dictionary<Type, String> dataMapper
        {
            get
            {
                // Add the rest of your CLR Types to SQL Types mapping here
                Dictionary<Type, String> dataMapper = new Dictionary<Type, string>();
                dataMapper.Add(typeof(int), "BIGINT");
                dataMapper.Add(typeof(string), "NVARCHAR(500)");
                dataMapper.Add(typeof(bool), "BIT");
                dataMapper.Add(typeof(DateTime), "DATETIME");
                dataMapper.Add(typeof(float), "FLOAT");
                dataMapper.Add(typeof(decimal), "DECIMAL(18,0)");
                dataMapper.Add(typeof(Guid), "UNIQUEIDENTIFIER");

                return dataMapper;
            }
        }

        public List<KeyValuePair<String, Type>> Fields
        {
            get { return this._fieldInfo; }
            set { this._fieldInfo = value; }
        }

        public string ClassName
        {
            get { return this._className; }
            set { this._className = value; }
        }

        public TableClass(Type t)
        {
            this._className = t.Name;

            foreach (PropertyInfo p in t.GetProperties())
            {
                KeyValuePair<String, Type> field = new KeyValuePair<String, Type>(p.Name, p.PropertyType);

                this.Fields.Add(field);
            }
        }

        public string CreateTableScript()
        {
            System.Text.StringBuilder script = new StringBuilder();

            script.AppendLine("CREATE TABLE " + this.ClassName);
            script.AppendLine("(");
            script.AppendLine("\t ID BIGINT,");
            for (int i = 0; i < this.Fields.Count; i++)
            {
                KeyValuePair<String, Type> field = this.Fields[i];

                if (dataMapper.ContainsKey(field.Value))
                {
                    script.Append("\t " + field.Key + " " + dataMapper[field.Value]);
                }
                else
                {
                    // Complex Type? 
                    script.Append("\t " + field.Key + " BIGINT");
                }

                if (i != this.Fields.Count - 1)
                {
                    script.Append(",");
                }

                script.Append(Environment.NewLine);
            }

            script.AppendLine(")");

            return script.ToString();
        }
    }
}

Ich habe diese Klassen in eine Assembly eingefügt, um es zu testen:

public class FakeDataClass
{
    public int AnInt
    {
        get;
        set;
    }

    public string AString
    {
        get;
        set;
    }

    public float AFloat
    {
        get;
        set;
    }

    public FKClass AFKReference
    {
        get;
        set;
    }
}

public class FKClass
    {
        public int AFKInt
        {
            get;
            set;
        }
    }

Und es generierte das folgende SQL:

CREATE TABLE FakeDataClass
(
         ID BIGINT,
         AnInt BIGINT,
         AString NVARCHAR(255),
         AFloat FLOAT,
         AFKReference BIGINT
)


CREATE TABLE FKClass
(
         ID BIGINT,
         AFKInt BIGINT
)


GO
ALTER TABLE FakeDataClass WITH NOCHECK
ADD CONSTRAINT FK_AFKReference FOREIGN KEY (AFKReference) REFERENCES FKClass(ID)
GO

Einige weitere Gedanken ... Ich würde darüber nachdenken, Ihren Klassen ein Attribut wie [SqlTable] hinzuzufügen, damit nur Tabellen für die gewünschten Klassen generiert werden.Außerdem kann hier viel aufgeräumt, Fehler behoben, optimiert (der FK Checker ist ein Witz) usw. usw. Nur um Ihnen den Einstieg zu erleichtern.

Andere Tipps

@Jonathan Holland

Wow, ich denke, das ist die roheste Arbeit, die ich je in einem StackOverflow-Beitrag gesehen habe.Gut gemacht. Jedoch, Anstatt DDL-Anweisungen als Zeichenfolgen zu konstruieren, sollten Sie unbedingt die verwenden SQL Server-Verwaltungsobjekte Klassen, die mit SQL 2005 eingeführt wurden.

David Hayden hat einen Beitrag mit dem Titel Erstellen Sie eine Tabelle in SQL Server 2005 mit C# und SQL Server Management Objects (SMO) – Codegenerierung Hier erfahren Sie, wie Sie mit SMO eine Tabelle erstellen.Die stark typisierten Objekte machen es mit Methoden wie den folgenden zum Kinderspiel:

// Create new table, called TestTable
Table newTable = new Table(db, "TestTable");

Und

// Create a PK Index for the table
Index index = new Index(newTable, "PK_TestTable");
index.IndexKeyType = IndexKeyType.DriPrimaryKey;

VanOrman, wenn Sie SQL 2005 verwenden, machen Sie SMO auf jeden Fall zu einem Teil Ihrer Lösung.

Probieren Sie meine CreateSchema-Erweiterungsmethode für Objekte unter aus http://createschema.codeplex.com/

Es gibt eine Zeichenfolge für jedes Objekt zurück, das CREATE TABLE-Skripte enthält.

Ich denke, bei komplexen Datentypen sollten Sie diese erweitern, indem Sie eine ToDB()-Methode angeben, die ihre eigene Implementierung zum Erstellen von Tabellen in der Datenbank enthält und auf diese Weise automatisch rekursiv wird.

Ab 2016 (glaube ich) können Sie Entity Framework 6 Code First verwenden, um ein SQL-Schema aus Poco-C#-Klassen zu generieren, oder Database First verwenden, um C#-Code aus SQL zu generieren.Komplettlösung von Code First bis DB

Bei komplexen Typen können Sie jeden einzelnen Typ, auf den Sie stoßen, rekursiv in eine eigene Tabelle umwandeln und dann versuchen, Fremdschlüsselbeziehungen zu verwalten.

Möglicherweise möchten Sie auch eine Vorabspezifizierung vornehmen welche Klassen werden in Tabellen umgewandelt oder nicht.Für komplexe Daten, die in der Datenbank wiedergegeben werden sollen, ohne das Schema aufzublähen, können Sie eine oder mehrere Tabellen für verschiedene Typen haben.In diesem Beispiel werden bis zu 4 verwendet:

CREATE TABLE MiscTypes /* may have to include standard types as well */
 ( TypeID INT,
   TypeName VARCHAR(...)
 )

CREATE TABLE MiscProperties
 ( PropertyID INT,
   DeclaringTypeID INT, /* FK to MiscTypes */
   PropertyName VARCHAR(...),
   ValueTypeID INT /* FK to MiscTypes */
 )

CREATE TABLE MiscData
 (  ObjectID INT,
    TypeID  INT
 )

CREATE TABLE MiscValues
 ( ObjectID INT, /* FK to MiscData*/
   PropertyID INT,
   Value VARCHAR(...)
 )

Sie können hier das Gegenteil tun, Datenbanktabelle zu C#-Klassen:http://pureobjects.com/dbCode.aspx

Auch...Vielleicht können Sie ein Tool wie Visio verwenden (ich bin mir nicht sicher, ob Visio dies kann, aber ich denke, dass dies der Fall ist), um Ihre Klassen in UML umzuwandeln und dann die UML zum Generieren des DB-Schemas zu verwenden ...oder vielleicht ein Tool wie dieses verwenden http://www.tangiblearchitect.net/visual-studio/

Ich weiß, dass Sie nach einer vollständigen Persistenzschicht suchen, aber die hbm2ddl-Aufgabe von NHibernate kann dies fast einzeilig tun.

Da ist ein NAnt-Aufgabe verfügbar, um es anzurufen, was durchaus von Interesse sein könnte.

Unterschall ist auch eine weitere Option.Ich verwende es oft, um Entitätsklassen zu generieren, die einer Datenbank zugeordnet sind.Es verfügt über ein Befehlszeilenprogramm, mit dem Sie Tabellen, Typen und viele andere nützliche Dinge angeben können

Probieren Sie DaoliteMappingTool für .net aus.Es kann Ihnen beim Generieren der Klassen helfen.Formular herunterladen Hier

Es gibt eine kostenlose App, Schematrix, die Klassen aus DB generiert. Sehen Sie sich an, ob das auch umgekehrt funktioniert :) http://www.schematrix.com/products/schemacoder/download.aspx

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top