如何从 C# 类生成数据库表?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47239/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 09:59:09  来源:igfitidea点击:

How can I generate database tables from C# classes?

提问by VanOrman

Does anyone know a way to auto-generate database tables for a given class? I'm not looking for an entire persistence layer - I already have a data access solution I'm using, but I suddenly have to store a lot of information from a large number of classes and I really don't want to have to create all these tables by hand. For example, given the following class:

有谁知道为给定类自动生成数据库表的方法?我不是在寻找一个完整的持久层——我已经有了一个我正在使用的数据访问解决方案,但我突然不得不存储来自大量类的大量信息,我真的不想创建所有这些桌子都是手工制作的。例如,给定以下类:

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

I'd expect the following SQL:

我期望以下 SQL:

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

I'm also wondering how you could handle complex types. For example, in the previously cited class, if we changed that to be :

我也想知道你如何处理复杂的类型。例如,在前面引用的类中,如果我们将其更改为:

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?

我怎么能处理这个?

I've looked at trying to auto-generate the database scripts by myself using reflection to enumerate through each class' properties, but it's clunky and the complex data types have me stumped.

我曾尝试使用反射来枚举每个类的属性,尝试自己自动生成数据库脚本,但它很笨重,而且复杂的数据类型让我感到困惑。

采纳答案by FlySwat

It's really late, and I only spent about 10 minutes on this, so its extremely sloppy, however it does work and will give you a good jumping off point:

真的很晚了,我只花了大约 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();
        }
    }
}

I put these classes in an assembly to test it:

我将这些类放在一个程序集中进行测试:

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

And it generated the following SQL:

它生成了以下 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

Some further thoughts...I'd consider adding an attribute such as [SqlTable] to your classes, that way it only generates tables for the classes you want. Also, this can be cleaned up a ton, bugs fixed, optimized (the FK Checker is a joke) etc etc...Just to get you started.

一些进一步的想法......我会考虑向您的类添加一个属性,例如 [SqlTable],这样它只会为您想要的类生成表。此外,这可以大量清理、修复错误、优化(FK Checker 是个笑话)等等……只是为了让您入门。

回答by Mark Cidade

For complex types, you can recursively convert each one that you come across into a table of its own and then attempt to manage foreign key relationships.

对于复杂类型,您可以递归地将遇到的每个类型转换为自己的表,然后尝试管理外键关系。

You may also want to pre-specify whichclasses will or won't be converted to tables. As for complex data that you want reflected in the database without bloating the schema, you can have one or more tables for miscellaneous types. This example uses as many as 4:

您可能还想预先指定哪些类将或不会转换为表。对于您希望在不使架构膨胀的情况下反映在数据库中的复杂数据,您可以拥有一个或多个用于杂项类型的表。此示例使用多达 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(...)
 )

回答by Vaibhav

I think for complex data types, you should extend them by specifying a ToDB() method which holds their own implementation for creating tables in the DB, and this way it becomes auto-recursive.

我认为对于复杂的数据类型,您应该通过指定一个 ToDB() 方法来扩展它们,该方法保存它们自己在数据库中创建表的实现,这样它就变成了自动递归。

回答by Vaibhav

Also... maybe you can use some tool such as Visio (not sure if Visio does this, but I think it does) to reverse engineer your classes into UML and then use the UML to generate the DB Schema... or maybe use a tool such as this http://www.tangiblearchitect.net/visual-studio/

另外...也许您可以使用诸如 Visio 之类的工具(不确定 Visio 是否这样做,但我认为确实如此)将您的类反向工程为 UML,然后使用 UML 生成 DB 模式...或者可能使用像这样的工具http://www.tangiblearchitect.net/visual-studio/

回答by Cheekysoft

I know you're looking for an entire persistence layer, but NHibernate's hbm2ddl task can do this almost as a one-liner.

我知道您正在寻找一个完整的持久层,但是 NHibernate 的 hbm2ddl 任务几乎可以作为一个单线来完成。

There is a NAnt taskavailable to call it which may well be of interest.

有一个NAnt 任务可以调用它,这可能很有趣。

回答by Portman

@Jonathan Holland

@乔纳森荷兰

Wow, I think that's the most raw work I've ever seen put into a StackOverflow post. Well done. However, instead of constructing DDL statements as strings, you should definitely use the SQL Server Management Objectsclasses introduced with SQL 2005.

哇,我认为这是我见过的 StackOverflow 帖子中最原始的工作。做得好。但是,您绝对应该使用SQL 2005 中引入的SQL Server 管理对象类,而不是将 DDL 语句构造为字符串。

David Hayden has a post entitled Create Table in SQL Server 2005 Using C# and SQL Server Management Objects (SMO) - Code Generationthat walks through how to create a table using SMO. The strongly-typed objects make it a breeze with methods like:

David Hayden 发表了一篇题为使用 C# 和 SQL Server 管理对象 (SMO) 在 SQL Server 2005 中创建表 - 代码生成的博文,其中介绍了如何使用 SMO 创建表。强类型对象使用以下方法轻而易举:

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

and

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

VanOrman, if you're using SQL 2005, definitely make SMO part of your solution.

VanOrman,如果您使用的是 SQL 2005,那么一定要让 SMO 成为您解决方案的一部分。

回答by IanStallings

Subsonicis also another option. I often use it to generate entity classes that map to a database. It has a command line utility that lets you specify tables, types, and a host of other useful things

亚音速也是另一种选择。我经常使用它来生成映射到数据库的实体类。它有一个命令行实用程序,可让您指定表、类型和许多其他有用的东西

回答by IanStallings

Try DaoliteMappingTool for .net. It can help you generate the classes. Download form Here

尝试用于 .net 的 DaoliteMappingTool。它可以帮助您生成类。在这里下载表格

回答by IanStallings

You can do the opposite, database table to C# classes here: http://pureobjects.com/dbCode.aspx

您可以在此处对 C# 类执行相反的数据库表操作:http: //pureobjects.com/dbCode.aspx

回答by Kai Sevelin

Try out my CreateSchema extension method for objects at http://createschema.codeplex.com/

http://createschema.codeplex.com/ 上试试我的 CreateSchema 对象扩展方法

It returns a string for any object containing CREATE TABLE scripts.

它为包含 CREATE TABLE 脚本的任何对象返回一个字符串。