Подтвердить что ты не робот

Как я могу генерировать таблицы базы данных из классов С#?

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

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

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

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

4b9b3361

Ответ 1

Это очень поздно, и я потратил около 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 - шутка) и т.д. И т.д. Просто, чтобы вы начали.

Ответ 2

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

Ничего себе, я думаю, что самая сырая работа, которую я когда-либо видел, помещала в сообщение StackOverflow. Отлично сработано. Однако вместо того, чтобы создавать операторы DDL в виде строк, вы должны обязательно использовать классы SQL Server Management Objects, введенные с SQL 2005.

У Дэвида Хейдена есть сообщение под названием Создать таблицу в SQL Server 2005 с использованием объектов управления С# и 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 частью своего решения.

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

Здесь вы можете сделать обратную таблицу базы данных на классы С#: http://pureobjects.com/dbCode.aspx

Ответ 8

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

Ответ 9

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

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

Ответ 10

@PortMan,

SQL DataObjects классные, я не знал о них. Это определенно заставило бы некоторые из грубой работы из моего кода.

На самом деле, мой оригинальный код был намного опрятным и распространялся по 3 классам, но после его работы я решил объединить все это в одну пасту для этого.

Я бы предпочел добавить атрибут [SqlTable] к классам домена, а также [PrimaryKey] к основному ключу ID вместо того, чтобы иметь код, автогенерирующий идентификатор.

Таким образом, код сможет правильно помещать ограничения PK, Index и FK в правильные столбцы.

Тем не менее, VanOrMan, вероятно, мог бы использовать мой код, чтобы генерировать 99% того, что ему нужно, а затем просто настроить SQL в редакторе перед его запуском.

Ответ 11

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

Ответ 12

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