Domanda

Qualcuno conosce un modo per generare automaticamente tabelle di database per una determinata classe?Non sto cercando un intero livello di persistenza: ho già una soluzione di accesso ai dati che sto utilizzando, ma all'improvviso devo archiviare molte informazioni da un gran numero di classi e non voglio davvero dover creare tutti questi tavoli a mano.Ad esempio, data la seguente classe:

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; }
    }
}

Mi aspetterei il seguente SQL:

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

Mi chiedo anche come potresti gestire tipi complessi.Ad esempio, nella classe precedentemente citata, se la cambiassimo in:

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; }
    }
}

Come potrei gestirlo?

Ho provato a generare automaticamente gli script del database da solo utilizzando la riflessione per enumerare le proprietà di ciascuna classe, ma è goffo e i tipi di dati complessi mi hanno sconcertato.

È stato utile?

Soluzione

È davvero tardi e ci ho dedicato solo circa 10 minuti, quindi è estremamente sciatto, tuttavia funziona e ti darà un buon punto di partenza:

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();
        }
    }
}

Ho inserito queste classi in un assembly per testarlo:

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;
        }
    }

E ha generato il seguente 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

Alcune ulteriori riflessioni... prenderei in considerazione l'aggiunta di un attributo come [SqlTable] alle tue classi, in questo modo genera solo tabelle per le classi che desideri.Inoltre, questo può essere ripulito moltissimo, corretto i bug, ottimizzato (l'FK Checker è uno scherzo) ecc. Ecc... Giusto per iniziare.

Altri suggerimenti

@Jonathan Olanda

Wow, penso che sia il lavoro più grezzo che abbia mai visto inserito in un post di StackOverflow.Ben fatto. Tuttavia, invece di costruire istruzioni DDL come stringhe, dovresti assolutamente usare il file Oggetti di gestione di SQL Server classi introdotte con SQL 2005.

David Hayden ha un post intitolato Creare una tabella in SQL Server 2005 utilizzando C# e SQL Server Management Objects (SMO) - Generazione di codice che illustra come creare una tabella utilizzando SMO.Gli oggetti fortemente tipizzati lo rendono un gioco da ragazzi con metodi come:

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

E

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

VanOrman, se utilizzi SQL 2005, rendi sicuramente SMO parte della tua soluzione.

Prova il mio metodo di estensione CreateSchema per gli oggetti su http://createschema.codeplex.com/

Restituisce una stringa per qualsiasi oggetto contenente script CREATE TABLE.

Penso che per i tipi di dati complessi, dovresti estenderli specificando un metodo ToDB() che contiene la propria implementazione per la creazione di tabelle nel DB e in questo modo diventa auto-ricorsivo.

A partire dal 2016 (credo), puoi utilizzare Entity Framework 6 Code First per generare schemi SQL da classi poco C# o utilizzare Database First per generare codice C# da SQL.Procedura dettagliata da Code First a DB

Per i tipi complessi, puoi convertire ricorsivamente ognuno di quelli che incontri in una tabella a parte e quindi tentare di gestire le relazioni di chiave esterna.

Potresti anche voler pre-specificare Quale le classi verranno convertite o meno in tabelle.Per quanto riguarda i dati complessi che si desidera vengano riflessi nel database senza gonfiare lo schema, è possibile avere una o più tabelle per tipi vari.Questo esempio ne utilizza fino a 4:

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(...)
 )

Puoi fare il contrario, dalla tabella del database alle classi C# qui:http://pureobjects.com/dbCode.aspx

Anche...forse puoi usare qualche strumento come Visio (non sono sicuro che Visio lo faccia, ma penso che lo faccia) per decodificare le tue classi in UML e quindi usare UML per generare lo schema DB...o magari utilizzare uno strumento come questo http://www.tangiblearchitect.net/visual-studio/

So che stai cercando un intero livello di persistenza, ma l'attività hbm2ddl di NHibernate può farlo quasi come una riga.

C'è un Compito Nant disponibile a chiamarlo, il che potrebbe essere interessante.

Subsonico è anche un'altra opzione.Lo uso spesso per generare classi di entità mappate su un database.Ha un'utilità da riga di comando che ti consente di specificare tabelle, tipi e una serie di altre cose utili

Prova DaoliteMappingTool per .net.Può aiutarti a generare le classi.Scarica il modulo Qui

C'è un'app gratuita, Schematrix che genera classi da DB, controlla se fa anche il contrario :) http://www.schematrix.com/products/schemacoder/download.aspx

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top