关于代码生成:如何从C#类生成数据库表?

关于代码生成:如何从C#类生成数据库表?

How can I generate database tables from C# classes?

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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:

1
2
3
4
5
CREATE TABLE Foo
(
    Property1 VARCHAR(500),
    Property2 INT
)

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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分钟的时间,所以它非常草率,但是它确实起作用,并且会给您一个很好的起点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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();
        }
    }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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帖子中见过的最原始的工作。做得好。但是,一定不要使用随SQL 2005引入的SQL Server管理对象类,而不是将DDL语句构造为字符串。

David Hayden的帖子标题为使用C#和SQL Server管理对象(SMO)在SQL Server 2005中创建表-代码生成,其中逐步介绍了如何使用SMO创建表。使用以下方法可以轻松实现强类型对象:

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

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

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


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

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


我认为对于复杂的数据类型,应该通过指定ToDB()方法来扩展它们,该方法拥有自己的实现,用于在数据库中创建表,这样它就可以自动递归。


从2016年开始(我认为),您可以使用Entity Framework 6 Code First从poco c#类生成SQL模式或使用Database First从sql生成c#代码。
代码优先到数据库演练


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

您可能还需要预先指定将或不将哪些类转换为表。至于要在数据库中反映而不使架构膨胀的复杂数据,您可以具有一个或多个其他类型的表。此示例使用多达4个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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(...)
 )

有一个免费的应用程序Schematrix,它可以从数据库生成类,还可以检查是否相反:) http://www.schematrix.com/products/schemacoder/download.aspx


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


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


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


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

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


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


推荐阅读