Entity Frameworkのコードファーストコンセプトを使用して、新しいデータベースを作成しようとしています。ただし、コードを実行すると、データベースは作成されません(DropCreateDatabaseIfModelChanges
設定を使用)。ただし、コードは正常に実行されます。データベースから何かを取得しようとすると、次の例外が表示されます。
私のプロジェクトは、一般的なサービスとリポジトリの構築を備えた別個のDataAccess
レイヤーを使用してセットアップされています。したがって、すべてのエンティティ、リポジトリ、およびデータベースコンテキストは、ソリューション内の別のプロジェクトにあります。
global.asax
ファイルには、次のコードが含まれています。
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyContext>());
新しいデータベースが存在しない場合は、初期化する必要がありますよね?
データベースコンテキストクラスは次のようになります。
namespace Website.DAL.Model
{
public class MyContext : DbContext
{
public IDbSet<Project> Projects { get; set; }
public IDbSet<Portfolio> Portfolios { get; set; }
/// <summary>
/// The constructor, we provide the connectionstring to be used to it's base class.
/// </summary>
public MyContext()
: base("MyConnectionString")
{
}
static MyContext()
{
try
{
Database.SetInitializer<MyContext>(new DropCreateDatabaseIfModelChanges<MyContext>());
}
catch (Exception)
{
throw;
}
}
/// <summary>
/// This method prevents the plurarization of table names
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention>();
}
}
}
インターネット上のいくつかのチュートリアルと記事に従って、このクラスを作成しました。それはすべて私にとって新しいですが、私が見ることができる限り、すべてはこれまでのところ正しいようです。だから今私が使用している2つのエンティティ。それらは「プロジェクト」および「ポートフォリオ」と呼ばれます。彼らはこのように見えます。
public class Portfolio
{
[Key]
public Guid Id { get; set; }
public String Name { get; set; }
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public bool IsPublished { get; set; }
public virtual ICollection<Project> Projects { get; set; }
}
そして
public class Project
{
[Key]
public Guid Id { get; set; }
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
public bool IsPublished { get; set; }
public String Title { get; set; }
}
私が使用しているデータベースは外部サーバーで実行されており、使用しているホスティングプロバイダーに付属しています。 SQL Serverデータベースを起動して実行していますが、データベースへの接続文字列はWebサイトプロジェクトのweb.config
にあります。私はすでにデータベースを削除してコードを再作成させようとしましたが、残念ながら機能しませんでした。ここで明らかな何かを見逃していますか?それとも、データベースを作成するためのサーバーへのアクセス権として単純なことでしょうか?
注:Database-Update -Script
コマンドを実行してSQLコードを生成すると、すべてのテーブルを作成するための正しいSQLステートメントが作成されたようです。
PDATE 1:さて、コメントのおかげでもう少し先に来ました。いくつかの変更を強制するためにエンティティに2つのプロパティを追加し、このようなカスタム初期化子も作成しました。
public class ForceDeleteInitializer : IDatabaseInitializer<MyContext>
{
private readonly IDatabaseInitializer<MyContext> _initializer = new DropCreateDatabaseIfModelChanges<MyContext>();
public ForceDeleteInitializer()
{
//_initializer = new ForceDeleteInitializer();
}
public void InitializeDatabase(MyContext context)
{
//This command is added to prevent open connections. See http://stackoverflow.com/questions/5288996/database-in-use-error-with-entity-framework-4-code-first
context.Database.ExecuteSqlCommand("ALTER DATABASE borloOntwikkel SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
_initializer.InitializeDatabase(context);
}
}
また、コンテキストのコンストラクターから初期化子を削除したため、このコード行を削除しました。
Database.SetInitializer<MyContext>(new DropCreateDatabaseIfModelChanges<MyContext>());
その後、これら3行をGlobal.asaxファイルに追加しました。
Database.SetInitializer(new ForceDeleteInitializer());
MyContext c = new MyContext();
c.Database.Initialize(true);
デバッグ時に、この例外が発生しています。
これにより、次の情報が得られます:
これらのアクションの後、データベースにアクセスできないため、ほとんどの場合削除されます。
これについておそらく何ができるでしょうか?私のホスティングプロバイダーが適切なアクセス権を私に与えないため、マスターデータベースにアクセスできない可能性が最も高いでしょう。
他の解決策がなかったため、アプローチを変更することにしました。
最初に自分でデータベースを作成し、正しいSQLユーザーが構成され、アクセスできることを確認しました。
次に、Global.asaxファイルから初期化子とコードを削除しました。その後、パッケージマネージャーコンソールで次のコマンドを実行しました(階層化されたデザインのため、コンソールで正しいプロジェクトを選択する必要があったため)。
Enable-Migrations
移行が有効になり、エンティティに最後の変更を加えた後、以下のコマンドを実行して新しい移行の足場を作りました。
Add-Migration AddSortOrder
移行が作成された後、コンソールで次のコマンドを実行すると、データベースがエンティティで更新されました。
Update-Database -Verbose
移行を実行するときにデータベースをシードできるように、移行を有効にするときに作成されたConfiguraton.csクラスのSeedメソッドをオーバーライドしました。このメソッドの最終コードはこの;
protected override void Seed(MyContext context)
{
// This method will be called after migrating to the latest version.
//Add menu items and pages
if (!context.Menu.Any() && !context.Page.Any())
{
context.Menu.AddOrUpdate(new Menu()
{
Id = Guid.NewGuid(),
Name = "MainMenu",
Description = "Some menu",
IsDeleted = false,
IsPublished = true,
PublishStart = DateTime.Now,
LastModified = DateTime.Now,
PublishEnd = null,
MenuItems = new List<MenuItem>()
{
new MenuItem()
{
Id = Guid.NewGuid(),
IsDeleted = false,
IsPublished = true,
PublishStart = DateTime.Now,
LastModified = DateTime.Now,
PublishEnd = null,
Name = "Some menuitem",
Page = new Page()
{
Id = Guid.NewGuid(),
ActionName = "Some Action",
ControllerName = "SomeController",
IsPublished = true,
IsDeleted = false,
PublishStart = DateTime.Now,
LastModified = DateTime.Now,
PublishEnd = null,
Title = "Some Page"
}
},
new MenuItem()
{
Id = Guid.NewGuid(),
IsDeleted = false,
IsPublished = true,
PublishStart = DateTime.Now,
LastModified = DateTime.Now,
PublishEnd = null,
Name = "Some MenuItem",
Page = new Page()
{
Id = Guid.NewGuid(),
ActionName = "Some Action",
ControllerName = "SomeController",
IsPublished = true,
IsDeleted = false,
PublishStart = DateTime.Now,
LastModified = DateTime.Now,
PublishEnd = null,
Title = "Some Page"
}
}
}
});
}
if (!context.ComponentType.Any())
{
context.ComponentType.AddOrUpdate(new ComponentType()
{
Id = Guid.NewGuid(),
IsDeleted = false,
IsPublished = true,
LastModified = DateTime.Now,
Name = "MyComponent",
PublishEnd = null,
PublishStart = DateTime.Now
});
}
try
{
// Your code...
// Could also be before try if you know the exception occurs in SaveChanges
context.SaveChanges();
}
catch (DbEntityValidationException e)
{
//foreach (var eve in e.EntityValidationErrors)
//{
// Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
// eve.Entry.Entity.GetType().Name, eve.Entry.State);
// foreach (var ve in eve.ValidationErrors)
// {
// Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"",
// ve.PropertyName, ve.ErrorMessage);
// }
//}
//throw;
var outputLines = new List<string>();
foreach (var eve in e.EntityValidationErrors)
{
outputLines.Add(string.Format(
"{0}: Entity of type \"{1}\" in state \"{2}\" has the following validation errors:",
DateTime.Now, eve.Entry.Entity.GetType().Name, eve.Entry.State));
foreach (var ve in eve.ValidationErrors)
{
outputLines.Add(string.Format(
"- Property: \"{0}\", Error: \"{1}\"",
ve.PropertyName, ve.ErrorMessage));
}
}
System.IO.File.AppendAllLines(@"c:\temp\errors.txt", outputLines);
throw;
}
}
現時点での欠点は、パッケージマネージャーコンソールで2つのコマンド(のみ)を使用して手動で移行する必要があることです。しかし、同時に、これが動的に行われないという事実も、データベースへの望ましくない変更を防ぐことができるため、良いことです。さらに、すべてが完璧に機能します。
詳細な質問については+1。
接続文字列が正しいデータベースを指していることを確認し、データベースにアクセスするために次のような許可属性を追加します。
<add name="PatientContext" providerName="System.Data.SqlClient" connectionString="Server=SQLSERVER2; Database=Patients; uid=PatientUser; password=123456; Integrated Security=False;" />
初期化する前に、次のコードを実行してデータベースを作成します。
context.Database.CreateIfNotExists();
したがって、最初にコンテキストクラスのコンストラクタパラメータからDb名を削除し、接続文字列にDb_nameを指定してから、ソリューションを再構築し、アプリケーションを実行して、アプリケーション用のデータベースを作成します。
例えば :
コンストラクターパラメーターにDb-Nameを渡していない
public EmployeeContext()
: base()
{
Database.SetInitializer<EmployeeContext>(new DropCreateDatabaseIfModelChanges<EmployeeContext>());
}
接続文字列では、次のようにDb-nameを渡します。
<add name="EmployeeContext" connectionString="server=.; database=EFCodeFirstTPHInheritance; uid=sa;Password=crius@123;persistsecurityinfo=True" providerName="System.Data.SqlClient"/>
</connectionStrings>