与此相关:有没有办法使用ServiceStack的ORMLite版本获取数据库中所有表的列表?
目前我正在使用我自己的班级,但如果已经有了这方面的话,我不想发明一个轮子.
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.这使我的所有表格都易于在我的项目中引用.
创建一个方法来检测模型程序集的更改:
既然我们有一个程序集,当我们为它构建新版本时,版本号会自动递增,我们需要能够检测应用程序中程序集的更改,以便我们可以重新创建表.
以下代码执行以下操作:
>确定模型装配的类型.因为它可以使用反射来确定当前程序集的版本号.
>检查上次创建的数据库模型版本的应用程序配置设置.
>如果找不到配置设置或版本号不匹配,则解析数据库连接.
>然后从程序集中推断出具有Model的表.这样做的好处是我们可以向Model程序集添加更多表,而不必更改drop / create代码.
>数据库删除并创建表.
>保存新的装配版本号.
>如果在不更改模型的情况下重新启动应用程序,数据库将保持不变,但是进行更改并重新启动并重新创建数据库.
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:禁用密钥检查约束(不推荐)
最基本的,是指示我们的数据库忽略约束,同时我们创建表.在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?
>直接内置于ORMLite:不.
>您可以使用db.Execsql在数据库中查询此信息.每个数据库都有不同的命令来执行此操作.以这种方式使用原始sql肯定是可能的.
希望这可以帮助.