Как я могу сгенерировать таблицы базы данных из классов 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; }
    }
}

How could I handle this?

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


person VanOrman    schedule 06.09.2008    source источник
comment
даже ищу такое же решение. Не могли бы вы помочь мне создать таблицы sql из классов. ?   -  person Keerthi Kumar    schedule 15.07.2015


Ответы (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 - шутка) и т.д ... Просто для начала.

person FlySwat    schedule 06.09.2008
comment
Не могу поверить, что использовал это в 2016 году. Большое спасибо за это! - person Gaspa79; 25.09.2016
comment
2019 - заселение - person jamesbascle; 09.06.2019
comment
Это отличная отправная точка для создания таблицы из класса C #. Если кому-то нужны документы о сопоставлении типа данных SQL и CLR, ниже приведена ссылка от Microsoft. docs.microsoft. com / en-us / dotnet / framework / data / adonet / sql / linq / - person Nikunj Ratanpara; 23.04.2020
comment
как передать имя сборки через аргумент? - person Zoyeb Shaikh; 01.07.2021

@ Джонатан Холланд

Вау, я думаю, что это самая грубая работа, которую я когда-либо видел, помещенную в сообщение StackOverflow. Отличная работа. Однако вместо того, чтобы строить операторы 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;

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

person Portman    schedule 06.09.2008
comment
Отличное предложение. Никогда раньше такого не видел. Ваша ссылка не работает, но вот еще один пример: codeproject.com/Articles/127065/ - person smoksnes; 16.03.2016

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

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

person Kai Sevelin    schedule 05.04.2012

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

person Vaibhav    schedule 06.09.2008

По состоянию на 2016 год (я думаю) вы можете использовать Entity Framework 6 Code First для генерации схемы SQL из классов poco c # или использовать Database First для генерации кода C # из sql. Пошаговое руководство Code First to DB

person tatigo    schedule 03.10.2016

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

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

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

person Vaibhav    schedule 06.09.2008

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

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

person Cheekysoft    schedule 06.09.2008

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

person IanStallings    schedule 07.09.2008

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

person Nitin    schedule 24.05.2015

Попробуйте DaoliteMappingTool для .net. Это может помочь вам сгенерировать классы. Загрузите форму здесь

person Community    schedule 06.01.2009