所以问题是:
是否有工具可以帮助我从C#类库创建数据库架构?
我并不寻求完美的工具(但如果有这样的工具,我会很高兴知道),但希望有一个至少可以帮助我创建起点的工具。
请注意,我希望您提出的解决方案最好参考.NET Framework 2.0版本,并基于传统实践(如T-SQL代码:
CREATE TABLE ...
),尽管每个建议都受到欢迎。CREATE TABLE ...
),尽管每个建议都受到欢迎。我不知道有什么工具可以做到这一点,但是对于那么多的表格,也许值得编写一个小工具,使用反射获取所有属性,并输出带有列名和类型定义的创建表语句。完美的脚本很难实现,但正如你在问题中所述,这可以为您提供一个非常好的起点,然后调整字段类型和大小。
大多数对象关系映射工具都包括一个选项,可以基于您映射的类创建数据库模式。
以下是使用NHibernate和Fluent NHibernate完成此操作的完整示例。这是一个独立的C#控制台应用程序,将映射一组C#业务对象,并根据它们创建数据库模式。
两个重要的注意事项:
这里是program.cs。请注意三个命名空间 - 一个包含程序本身,一个包含实体,一个包含Fluent NHibernate的映射覆盖示例(如果您的对象不遵循内置映射约定,则非常有用)
using System;
using System.Collections.Generic;
using System.IO;
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using Schematica.Entities;
namespace Schematica.ConsoleApp {
class Program {
const string SCHEMA_FILENAME = "schema.sql";
const string CONNECTION_STRING = "Data Source=spotgeek;Initial Catalog=dylanhax;Integrated Security=True";
public static void Main(string[] args) {
if (File.Exists(SCHEMA_FILENAME)) File.Delete(SCHEMA_FILENAME);
ConfigureNHibernate(CONNECTION_STRING, MapEntities);
Console.WriteLine("Exported schema to " + (Path.GetFullPath(SCHEMA_FILENAME)));
Console.ReadKey(false);
}
private static void MapEntities(MappingConfiguration map) {
// Notice how we're constraining the auto-mapping to only map those entities
// whose namespace ends with "Entities" - otherwise it'll try to
// auto-map every class in the same assembly as Customer.
map.AutoMappings.Add(
AutoMap.AssemblyOf<Customer>()
.Where(type => type.Namespace.EndsWith("Entities"))
.UseOverridesFromAssemblyOf<Customer>());
}
private static Configuration ConfigureNHibernate(string connectionString, Action<MappingConfiguration> mapper) {
var database = Fluently.Configure().Database(MsSqlConfiguration.MsSql2005.ConnectionString(connectionString));
return (database.Mappings(mapper).ExposeConfiguration(ExportSchema).BuildConfiguration());
}
private static void WriteScriptToFile(string schemaScript) {
File.AppendAllText(SCHEMA_FILENAME, schemaScript);
}
private static void ExportSchema(Configuration config) {
bool createObjectsInDatabase = false;
new SchemaExport(config).Create(WriteScriptToFile, createObjectsInDatabase);
}
}
}
// This demonstrates how to override auto-mapped properties if your objects don't
// adhere to FluentNH mapping conventions.
namespace Schematica.Mappings {
public class ProductMappingOverrides : IAutoMappingOverride<Product> {
public void Override(AutoMapping<Product> map) {
// This specifies that Product uses ProductCode as the primary key,
// instead of the default Id field.
map.Id(product => product.ProductCode);
}
}
}
// This is the namespace containing your business objects - the things you want to export to your database.
namespace Schematica.Entities {
public class Customer {
public virtual int Id { get; set; }
public virtual string Forenames { get; set; }
public virtual string Surname { get; set; }
}
public class Product {
public virtual Guid ProductCode { get; set; }
public virtual string Description { get; set; }
}
public class Order {
public virtual int Id { get; set; }
private IList<Product> products = new List<Product>();
public virtual IList<Product> Products {
get { return products; }
set { products = value; }
}
public virtual Customer Customer { get; set; }
}
}
以下是上述代码导出到schema.sql的内容:
if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FK952904EBD5E0278A]') AND parent_object_id = OBJECT_ID('[Product]'))
alter table [Product] drop constraint FK952904EBD5E0278A
if exists (select 1 from sys.objects where object_id = OBJECT_ID(N'[FKD1436656C882C014]') AND parent_object_id = OBJECT_ID('[Order]'))
alter table [Order] drop constraint FKD1436656C882C014
if exists (select * from dbo.sysobjects where id = object_id(N'[Customer]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [Customer]
if exists (select * from dbo.sysobjects where id = object_id(N'[Product]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [Product]
if exists (select * from dbo.sysobjects where id = object_id(N'[Order]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [Order]
create table [Customer] (
Id INT IDENTITY NOT NULL,
Forenames NVARCHAR(255) null,
Surname NVARCHAR(255) null,
primary key (Id)
)
create table [Product] (
ProductCode UNIQUEIDENTIFIER not null,
Description NVARCHAR(255) null,
Order_id INT null,
primary key (ProductCode)
)
create table [Order] (
Id INT IDENTITY NOT NULL,
Customer_id INT null,
primary key (Id)
)
alter table [Product]
add constraint FK952904EBD5E0278A
foreign key (Order_id)
references [Order]
alter table [Order]
add constraint FKD1436656C882C014
foreign key (Customer_id)
references [Customer]
最新的"Code First" Entity Framework CTP已经发布。在稍微编写一些代码后,您可以使用它直接从代码生成数据库。
如果要创建1000个表格,仍需要花费一些时间。
如果类结构是关系型的,并且相对简单,可以转换为SQL Create Table或EF模型,也许您可以编写自己的代码生成脚本来创建.edmx或SQL。
您可以使用T4或CodeDom,这可能比手动创建1000个表要快得多。
编辑:我在第一次撰写答案时忘记了这一点。不久前,我看到Rob Conery的一个视频演示了Subsonic Simple Repositroy。Simple Repository允许您将对象插入到DB中,如果没有表,则会为您创建一个表。
在此处查看视频 http://subsonicproject.com/docs/Using_SimpleRepository
仅仅是为了更详细地解释其他人所说的--大多数ORM类型的工具都有模型优先的功能。就像EF4、NHibernate和Telerik Open Access一样,Subsonic(http://www.subsonicproject.com/)有一个简单的存储库模式,可以从类生成表。
另一个选择是编写一个简单的基于反射的工具来遍历您的类。
虽然你没有问,但我对你的数据访问需求很好奇。我们使用关系数据库来执行三个主要任务:持久性、高效检索信息和声明性参照完整性(例如外键)。仅为每个类创建一个表只解决了问题的一半。
你的数据量大吗(即数百GB或多TB)?有很多读取操作吗?很多写入操作吗?你的类有自然主键吗?还是使用替代键?你的类之间定义了关系吗(例如员工
在工厂
中工作)。
类有很多字段吗?您是否有任何元数据指示精度的数据类型,即不仅是string Name
,而且Name
最多可以有80个字符并且不可为空?您只需要存储英语还是使用需要扩展字符集(如汉语)的语言?如果没有方法指示精度,则数据库将最终具有严重宽行(或潜在宽行)。许多数据库将最大行大小限制为8k-64k范围内的某些内容。宽行会影响读取和写入性能。使用text
字段可能会避开页面大小限制,但会导致更昂贵的读取性能。
与实施数据库结构一样重要的是对其进行索引并使用引用完整性。类/表的数量随时间增长吗?也许更抽象的结构与较少的表格更合适。
这只是我的2美分。希望这有所帮助。