59

特定のクラスのデータベース テーブルを自動生成する方法を知っている人はいますか? パーシスタンス レイヤー全体を探しているわけではありません。既に使用しているデータ アクセス ソリューションがありますが、突然、多数のクラスから大量の情報を保存する必要があり、作成する必要がありません。これらすべてのテーブルを手作業で。たとえば、次のクラスがあるとします。

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

どうすればこれを処理できますか?

リフレクションを使用して自分でデータベース スクリプトを自動生成して、各クラスのプロパティを列挙しようとしましたが、扱いにくく、複雑なデータ型に困惑しました。

4

12 に答える 12

103

本当に遅く、私はこれに約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 チェッカーはジョークです) なども可能です。

于 2008-09-06T05:50:12.650 に答える
13

@ジョナサン・ホランド

うわー、これは StackOverflow の投稿で見た中で最も未加工の作業だと思います。素晴らしい。ただし、DDL ステートメントを文字列として構築する代わりに、SQL 2005 で導入されたSQL Server Management Objectsクラスを確実に使用する必要があります。

David Hayden は、「 C# と SQL Server Management Objects (SMO) を使用した SQL Server 2005 でのテーブルの作成 - コード生成」というタイトルの投稿を行っており、 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 をソリューションの一部にしてください。

于 2008-09-06T11:33:14.370 に答える
4

http://createschema.codeplex.com/でオブジェクトの CreateSchema 拡張メソッドを試してください。

CREATE TABLE スクリプトを含むオブジェクトの文字列を返します。

于 2012-04-05T18:15:06.470 に答える
3

複雑なデータ型の場合は、DB にテーブルを作成するための独自の実装を保持する ToDB() メソッドを指定して拡張する必要があると思います。これにより、自動再帰になります。

于 2008-09-06T05:52:03.847 に答える
2

2016 年現在 (私が思うに)、Entity Framework 6 Code First を使用して poco c# クラスから SQL スキーマを生成したり、Database First を使用して SQL から C# コードを生成したりできます。 Code First から DB へのウォークスルー

于 2016-10-03T16:26:11.457 に答える
1

複合型の場合、遭遇したそれぞれを独自のテーブルに再帰的に変換してから、外部キー関係の管理を試みることができます。

また、どのクラスをテーブルに変換するか、または変換しないかを事前に指定することもできます。スキーマを肥大化させずにデータベースに反映させたい複雑なデータについては、さまざまなタイプのテーブルを 1 つ以上持つことができます。この例では、最大 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(...)
 )
于 2008-09-06T04:18:55.973 に答える
0

パーシスタンス レイヤー全体を探していることは承知していますが、NHibernate の hbm2ddl タスクはこれをほぼワンライナーで実行できます。

それを呼び出すために利用できるNAntタスクがあり、これは興味深いかもしれません。

于 2008-09-06T09:14:36.700 に答える
0

サブソニックも別のオプションです。データベースにマップするエンティティ クラスを生成するためによく使用します。テーブル、タイプ、およびその他の多くの便利なものを指定できるコマンドラインユーティリティがあります

于 2008-09-07T04:44:29.647 に答える
0

また...おそらく、Visioなどのツールを使用して(Visioがこれを行うかどうかはわかりませんが、そうすると思います)、クラスをUMLにリバースエンジニアリングし、UMLを使用してDBスキーマを生成できます...またはおそらく使用できますこのようなツールhttp://www.tangiblearchitect.net/visual-studio/

于 2008-09-06T05:56:53.320 に答える
-1

.net 用の DaoliteMappingTool を試してください。クラスを生成するのに役立ちます。フォームのダウンロードはこちら

于 2009-01-06T02:02:19.647 に答える
-2

ここで、C# クラスに対して反対のデータベース テーブルを実行できます: http://pureobjects.com/dbCode.aspx

于 2009-04-15T04:27:11.837 に答える