Как создать таблицы базы данных из классов C#?

кто-нибудь знает способ для автоматического создания таблиц базы данных для данного класса? Я не ищу весь уровень персистентности - у меня уже есть решение для доступа к данным, которое я использую, но мне внезапно приходится хранить много информации из большого количества классов, и я действительно не хочу создавать все эти таблицы вручную. Например, задан следующий класс:

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

Я ожидал бы следующий SQL:

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

мне тоже интересно, как вы могли обрабатывать сложные типы. Например, в ранее процитированном классе, если мы изменили это на:

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

как я мог справиться с этим?

Я посмотрел на попытку автоматического создания скриптов базы данных самостоятельно, используя отражение для перечисления свойств каждого класса, но это неуклюже, и сложные типы данных поставили меня в тупик.

12 ответов


это действительно поздно, и я только потратил около 10 минут на это, так что его очень неаккуратно, однако он работает и даст вам хорошую точку прыжка:

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

я помещаю эти классы в сборку, чтобы проверить ее:

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

и он создал следующий 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

некоторые дополнительные мысли...Я бы подумал о добавлении атрибута, такого как [SqlTable], в ваши классы, таким образом, он генерирует только таблицы для классов, которые вы хотите. Кроме того, это может быть убрали тонну, исправили ошибки, оптимизировали (FK Checker-шутка) и т. д...Просто для начала.


@Jonathan Holland

Вау, я думаю, что это самая сырая работа, которую я когда-либо видел в StackOverflow post. Молодец. , вместо построения операторов DDL в виде строк, вы должны определенно использовать объекты управления SQL Server классы, введенные с SQL 2005.

Дэвид Хейден имеет пост под названием создать таблицу в SQL Server 2005 с помощью объектов управления C# и SQL Server (SMO) - генерация кода что пошаговое руководство по созданию таблицы с помощью SMO. Строго типизированные объекты делают его ветер с такими методами, как:

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

и

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

VanOrman, если вы используете SQL 2005, определенно сделайте SMO частью своего решения.


попробуйте мой метод расширения CreateSchema для объектов вhttp://createschema.codeplex.com/

он возвращает строку для любого объекта, содержащего скрипты CREATE TABLE.


Я думаю, что для сложных типов данных, необходимо выразить их путем указания ToDB() метод, который имеет свои собственные реализации для создания таблиц в БД, и таким образом он становится автоматическим рекурсивным.


с 2016 года (я думаю) вы можете использовать код Entity Framework 6 Сначала для создания схемы SQL из классов poco c# или использовать базу данных для создания кода c# из sql. код сначала в DB walkthrough


для сложных типов вы можете рекурсивно преобразовать каждый из них в отдельную таблицу, а затем попытаться управлять отношениями внешнего ключа.

вы также можете заранее указать , который занятия будут или не будут преобразованы в таблицы. Что касается сложных данных, которые вы хотите отразить в базе данных, не раздувая схемы, вы можете иметь одну или несколько таблиц для разных типов. В этом примере используется целых 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(...)
 )

вы можете сделать противоположное, таблица базы данных для классов C# здесь: http://pureobjects.com/dbCode.aspx


также... возможно, вы можете использовать какой-то инструмент, такой как Visio (не уверен, что Visio делает это, но я думаю, что это так), чтобы перепроектировать ваши классы в UML, а затем использовать UML для создания схемы DB... или, может быть, использовать такой инструмент, как этот http://www.tangiblearchitect.net/visual-studio/


Я знаю, что вы ищете весь слой персистентности, но задача Hbm2ddl NHibernate может сделать это почти как однострочный.

есть NAnt task доступно для вызова, что вполне может представлять интерес.


дозвуковых есть и другой вариант. Я часто использую его для создания классов сущностей, которые сопоставляются с базой данных. Это утилита командной строки, которая позволяет указать таблицы, типы и множество других полезных вещей


попробуйте DaoliteMappingTool для .сеть. Это может помочь вам создать классы. Скачать форму здесь


есть бесплатное приложение Schematrix, которое генерирует классы из БД, проверьте, делает ли обратное тоже:)http://www.schematrix.com/products/schemacoder/download.aspx