是否存在检测代码模型中的更改并自动重新创建数据库的首选方法?我不需要迁移数据,如果有更改,可以完全删除所有表,从模型中重新创建表,并使用代码中的初始数据集填充新表,这样就可以了。
与此相关:使用ServiceStack的ORMLite版本是否可以获取数据库中所有表的列表?
目前,我正在使用自己的类(class),但是如果已经有了一些东西,我想不发明轮子。
Web.config:
<connectionStrings>
<add name="ApplicationServices"
connectionString="data source=(local);Integrated Security=SSPI;database=MyTestDatabase"
providerName="System.Data.SqlClient" />
</connectionStrings>
DatabaseUtil:
public class DatabaseUtil
{
private const int CURR_VERSION = 1;
private static string connString = WebConfigurationManager.ConnectionStrings["ApplicationServices"].ConnectionString;
public static void Init(Funq.Container container)
{
using (var db = connString.OpenDbConnection())
{
using (IDbConnection dbConn = connString.OpenDbConnection())
{
var createScript = !db.TableExists(typeof(ZatabaseConfig).Name.ToString());
if (!createScript)
{
var first = dbConn.FirstOrDefault<ZatabaseConfig>("");
createScript = first == null || first.Version < CURR_VERSION;
}
if (createScript)
{
DropAndCreateDatabase(dbConn);
}
}
// db.InsertAll(SeedData);
}
}
private static void DropAndCreateDatabase(IDbConnection dbConn)
{
var tables = new[] { typeof(Table1), typeof(Table2), typeof(Table3), typeof(ZatabaseConfig) };
// running drop tables only once doesn't remove tables that are referenced
using (var dbDrop = createConnection())
dbDrop.ExecuteSql(DROP_EVERYTHING_CONSTRAINT);
for (int i = 0; i < 5; i++)
{
// dropping table 5 times to eliminate foreign constraints
try
{
using (var dbNew = createConnection())
dbNew.ExecuteSql(DROP_EVERYTHING_TABLES);
}
catch
{
}
}
//Drop and re-create all Auth and registration tables
//var authRepo = (OrmLiteAuthRepository)container.Resolve<IUserAuthRepository>();
//authRepo.DropAndReCreateTables();
dbConn.CreateTables(true, tables);
dbConn.Insert(new ZatabaseConfig { ConfigId = (int)ZatabaseConfigIds.Version, Name = CURR_VERSION });
}
private static string DROP_EVERYTHING_CONSTRAINT = @"
SELECT 'ALTER TABLE ' + OBJECT_NAME(f.parent_object_id)+
' DROP CONSTRAINT ' + f.name
FROM .sys.foreign_keys AS f
INNER JOIN .sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id
";
private static string DROP_EVERYTHING_TABLES = @"
exec sp_MSforeachtable 'DROP TABLE ?'
";
}
最佳答案
我知道没有内置的机制可以做到这一点。但是您可以扮演自己的角色:
创建一个单独的模型装配:
如果您发现自己大量更改了模型,那么我建议您将模型创建为单独的装配。因此,在您的解决方案中创建一个新的库项目,然后在其中移动模型。然后在您的主项目中引用该项目。无论如何,这是良好的组织实践。
然后,在(模型的)Properties/AssemblyInfo.cs
中,确保已使用通配符内部版本号设置了AssemblyVersion
并删除了[assembly: AssemblyFileVersion ...]
(如果存在)。
[assembly: AssemblyVersion("1.0.*")]
所以我的模型:类看起来像这样:
using System;
using ServiceStack.Model;
using ServiceStack.DataAnnotations;
namespace Blog
{
public static class Model
{
public class Article : IHasId<int>
{
[AutoIncrement, PrimaryKey]
public int Id { get; set; }
...
注意,我使用了外部静态类
Model
。这使我所有的表在我的项目中都易于引用。创建一种方法来检测对模型部件的更改:
现在我们有了一个程序集版本号,当它进行新的构建时,版本号会自动增加,我们需要能够检测到应用程序中程序集的更改,以便我们重新创建表。
下面的代码执行以下操作:
public static void Init(Funq.Container container)
{
...
CheckDatabaseModel(typeof(Model));
}
public void CheckDatabaseModel(Type modelType)
{
// Get the referenced model version
string modelVersion = Assembly.GetAssembly(modelType).GetName().Version.ToString();
// Determine the last model version number from the configuration
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var lastModelVersion = config.AppSettings.Settings["DatabaseModelVersion"];
// Determine if the model has changed
if(lastModelVersion == null || lastModelVersion.Value != modelVersion)
{
Console.WriteLine("Model has changed");
using(var db = Resolve<IDbConnectionFactory>().OpenDbConnection())
{
// Drop and recreate the tables
// Determine the tables from the model assembly
var tables = modelType.GetNestedTypes();
db.DropAndCreateTables(tables);
// Repopulate database with initial data.
// Save the model version to settings
if(lastModelVersion == null)
config.AppSettings.Settings.Add("DatabaseModelVersion", modelVersion);
else
config.AppSettings.Settings["DatabaseModelVersion"].Value = modelVersion;
config.Save(ConfigurationSaveMode.Modified);
}
} else {
// The model numbers matched
Console.WriteLine("Model is current");
}
}
处理表创建顺序
您的数据库可能具有外键约束,然后您将发现需要按特定顺序创建表,否则数据库将不满意。
在手动指定
db.DropAndCreateTables
的表数组之前,您将指定创建顺序以满足所有约束。但是,因为我们使用的是modelTypes.GetNestedTypes()
,所以该命令不再处于我们的控制中。有两种方法可以解决此问题。1:禁用 key 检查约束(不推荐)
最基本的方法是指示我们的数据库在创建表时忽略约束。在MySQL中,代码为:
db.ExecuteSql("SET foreign_key_checks = 0;");
db.DropAndCreateTables(tables);
db.ExecuteSql("SET foreign_key_checks = 1;");
MSSQL或其他数据库中所需的代码将有所不同,在某些情况下可能无法实现。但这最终是做事的危险方式。毕竟,约束是有原因的。
2:在模型中定义表创建顺序(推荐):
我们可以创建一个简单的属性,用其装饰表,以告知数据库设置代码执行操作的顺序。这样做的好处是我们不必轮流使用约束,而且维护人员可以清楚地知道要执行的操作顺序发生在。
属性:
public class TableCreationOrderAttribute : Attribute
{
public int Order { get; private set; }
public TableCreationOrderAttribute(int order)
{
Order = order;
}
}
装饰模型:
public static class Model
{
[TableCreationOrder(3)]
public class Article : IHasId<int>
{
[AutoIncrement, PrimaryKey]
public int Id { get; set; }
...
因此,现在我们需要告诉数据库设置代码如何使用此顺序正确创建表。将此行
db.DropAndCreateTables(tables);
替换为:var orderedTables = new Dictionary<int, Type>();
var unorderedTables = new List<Type>(); // Tables without the attribute will be created last, but in no specific order
foreach(var table in tables)
{
var order = Attribute.GetCustomAttribute(table, typeof(TableCreationOrderAttribute)) as TableCreationOrderAttribute;
if(order != null)
orderedTables.Add(order.Order, table);
else
unorderedTables.Add(table);
}
foreach(var table in orderedTables.OrderBy(t=>t.Key))
db.DropAndCreateTable(table.Value);
foreach(var table in unorderedTables)
db.DropAndCreateTable(table);
概括
看起来很多,但事实并非如此,
CheckDatabaseModel
方法可以缩减为少于35行代码。它是通用的,因此您可以将其添加到实用程序库中,并在其他项目中使用一次调用再次使用它。您将不必担心手动导致触发数据库刷新。The full source code for the method can be found here。
包括简化的分步指南,因为此答案包括许多其他说明。
Is there a way to get a list all tables within the Database by using ServiceStack's version of ORMLite?
db.ExecSql
在数据库中查询此信息。每个数据库都有一个不同的命令来执行此操作。这样肯定可以使用原始SQL。 希望这可以帮助。
关于servicestack - ServiceStack OrmLite如何检测对代码模型的更改并重新创建数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21320486/