Pregunta

¿Alguien conoce una forma de generar automáticamente tablas de bases de datos para una clase determinada?No estoy buscando una capa de persistencia completa: ya tengo una solución de acceso a datos que estoy usando, pero de repente tengo que almacenar mucha información de una gran cantidad de clases y realmente no quiero tener que crear Todas estas tablas a mano.Por ejemplo, dada la siguiente clase:

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

Esperaría el siguiente SQL:

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

También me pregunto cómo se pueden manejar tipos complejos.Por ejemplo, en la clase citada anteriormente, si cambiamos eso para que sea:

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

¿Cómo podría manejar esto?

He intentado generar automáticamente los scripts de la base de datos usando la reflexión para enumerar las propiedades de cada clase, pero es complicado y los tipos de datos complejos me dejan perplejo.

¿Fue útil?

Solución

Es muy tarde y solo dediqué unos 10 minutos a esto, por lo que es extremadamente descuidado, sin embargo, funciona y te dará un buen punto de partida:

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

Puse estas clases en un ensamblado para probarlo:

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

Y generó el siguiente 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

Algunas ideas adicionales... Consideraría agregar un atributo como [SqlTable] a sus clases, de esa manera solo genera tablas para las clases que desea.Además, esto se puede limpiar muchísimo, corregir errores, optimizar (el FK Checker es una broma), etc., etc. Solo para comenzar.

Otros consejos

@Jonathan Holanda

Vaya, creo que es el trabajo más crudo que he visto en una publicación de StackOverflow.Bien hecho. Sin embargo, en lugar de construir declaraciones DDL como cadenas, definitivamente deberías usar el Objetos de administración de SQL Server clases introducidas con SQL 2005.

David Hayden tiene una publicación titulada Crear tabla en SQL Server 2005 usando C# y objetos de administración de SQL Server (SMO): generación de código que explica cómo crear una tabla usando SMO.Los objetos fuertemente tipados facilitan el uso de métodos como:

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

y

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

VanOrman, si está utilizando SQL 2005, definitivamente haga que SMO forme parte de su solución.

Pruebe mi método de extensión CreateSchema para objetos en http://createschema.codeplex.com/

Devuelve una cadena para cualquier objeto que contenga scripts CREATE TABLE.

Creo que para tipos de datos complejos, deberías ampliarlos especificando un método ToDB() que tenga su propia implementación para crear tablas en la base de datos, y de esta manera se vuelve autorrecursivo.

A partir de 2016 (creo), puede usar Entity Framework 6 Code First para generar un esquema SQL a partir de clases poco C# o usar Database First para generar código C# a partir de SQL.Tutorial de Code First to DB

Para tipos complejos, puede convertir recursivamente cada uno de los que encuentre en una tabla propia y luego intentar gestionar las relaciones de clave externa.

Es posible que también desee especificar previamente cual las clases se convertirán o no en tablas.En cuanto a los datos complejos que desea reflejar en la base de datos sin inflar el esquema, puede tener una o más tablas para tipos diversos.Este ejemplo utiliza hasta 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(...)
 )

Puedes hacer lo contrario, tabla de base de datos para clases de C# aquí:http://pureobjects.com/dbCode.aspx

También...tal vez puedas usar alguna herramienta como Visio (no estoy seguro si Visio hace esto, pero creo que sí) para aplicar ingeniería inversa a tus clases en UML y luego usar UML para generar el esquema de base de datos...o tal vez usar una herramienta como esta http://www.tangiblearchitect.net/visual-studio/

Sé que está buscando una capa de persistencia completa, pero la tarea hbm2ddl de NHibernate puede hacer esto casi como una sola línea.

Hay un tarea NAnt disponible para llamarlo que bien puede ser de su interés.

Subsónico También es otra opción.A menudo lo uso para generar clases de entidad que se asignan a una base de datos.Tiene una utilidad de línea de comandos que le permite especificar tablas, tipos y muchas otras cosas útiles.

Pruebe DaoliteMappingTool para .net.Puede ayudarte a generar las clases.Descargar formulario Aquí

Hay una aplicación gratuita, Schematrix, que genera clases a partir de una base de datos, comprueba si también hace lo contrario :) http://www.schematrix.com/products/schemacoder/download.aspx

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top