Как я могу генерировать таблицы базы данных из классов C#?
-
09-06-2019 - |
Вопрос
Кто-нибудь знает способ автоматического создания таблиц базы данных для данного класса?Мне не нужен целый уровень персистентности — у меня уже есть решение для доступа к данным, которое я использую, но мне внезапно приходится хранить много информации из большого количества классов, и мне действительно не хочется создавать все эти таблицы вручную.Например, учитывая следующий класс:
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; }
}
}
Как я мог справиться с этим?
Я рассматривал возможность самостоятельного автоматического создания сценариев базы данных с использованием отражения для перечисления свойств каждого класса, но это неуклюже, а сложные типы данных поставили меня в тупик.
Решение
Уже очень поздно, и я потратил на это всего около 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 - это шутка) и т. д. и т. п. Просто для начала.
Другие советы
@Джонатан Холланд
Ух ты, я думаю, что это самая сырая работа, которую я когда-либо видел в посте 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 в свое решение.
Попробуйте мой метод расширения CreateSchema для объектов по адресу http://createschema.codeplex.com/
Он возвращает строку для любого объекта, содержащего сценарии CREATE TABLE.
Я думаю, что для сложных типов данных вам следует расширить их, указав метод ToDB(), который содержит собственную реализацию для создания таблиц в БД, и таким образом он станет авторекурсивным.
Начиная с 2016 года (я думаю), вы можете использовать Entity Framework 6 Code First для генерации схемы SQL из классов poco C# или использовать Database First для генерации кода C# из sql.Пошаговое руководство Code First to DB
Для сложных типов вы можете рекурсивно преобразовать каждый из них, с которым вы сталкиваетесь, в отдельную таблицу, а затем попытаться управлять связями внешнего ключа.
Вы также можете заранее указать который классы будут или не будут преобразованы в таблицы.Что касается сложных данных, которые вы хотите отразить в базе данных, не раздувая схему, вы можете иметь одну или несколько таблиц для разных типов.В этом примере используется целых 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 для создания схемы БД...или, может быть, использовать такой инструмент, как этот http://www.tangiblearchitect.net/visual-studio/
Я знаю, что вам нужен целый уровень персистентности, но задача hbm2ddl NHibernate может сделать это почти как однострочник.
Eсть NAnt-задача доступен для вызова, что вполне может представлять интерес.
Дозвуковой это еще один вариант.Я часто использую его для создания классов сущностей, которые отображаются в базе данных.У него есть утилита командной строки, которая позволяет указывать таблицы, типы и множество других полезных вещей.
Попробуйте DaoliteMappingTool для .net.Это может помочь вам создать классы.Скачать форму Здесь
Есть бесплатное приложение Schematrix, которое генерирует классы из БД, проверьте, делает ли и обратное :) http://www.schematrix.com/products/schemacoder/download.aspx