EFのMigrationでDefaultValue制約を削除する

こんにちは。beaglesoftの真鍋です。

SQL Server上でEntityFramework4を利用したCodeFirst開発を行う時のことなのですが、NotNull制約(Require属性)を設定した列の定義をNullableへ変更するとき、MigrationでDefaultValue制約を削除する処理を追加しなければなりません。また、列の定義を変更するときも同様の問題にぶつかります。

ALTER TABLE ALTER COLUMN CityName は失敗しました。1 つ以上のオブジェクトがこの列を参照しています。
   場所 System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   場所 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   場所 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   場所 System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   場所 System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite)
   場所 System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
   場所 System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   場所 System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<NonQuery>b__0(DbCommand t, DbCommandInterceptionContext`1 c)
   場所 System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
   場所 System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(DbCommand command, DbCommandInterceptionContext interceptionContext)
   場所 System.Data.Entity.Internal.InterceptableDbCommand.ExecuteNonQuery()
   場所 System.Data.Entity.Migrations.DbMigrator.ExecuteSql(MigrationStatement migrationStatement, DbConnection connection, DbTransaction transaction, DbInterceptionContext interceptionContext)
   場所 System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.ExecuteSql(MigrationStatement migrationStatement, DbConnection connection, DbTransaction transaction, DbInterceptionContext interceptionContext)
   場所 System.Data.Entity.Migrations.DbMigrator.ExecuteStatementsInternal(IEnumerable`1 migrationStatements, DbConnection connection, DbTransaction transaction, DbInterceptionContext interceptionContext)
   場所 System.Data.Entity.Migrations.DbMigrator.ExecuteStatementsWithinTransaction(IEnumerable`1 migrationStatements, DbTransaction transaction, DbInterceptionContext interceptionContext)
   場所 System.Data.Entity.Migrations.DbMigrator.ExecuteStatementsWithinNewTransaction(IEnumerable`1 migrationStatements, DbConnection connection, DbInterceptionContext interceptionContext)
   場所 System.Data.Entity.Migrations.DbMigrator.ExecuteStatementsInternal(IEnumerable`1 migrationStatements, DbConnection connection, DbInterceptionContext interceptionContext)
   場所 System.Data.Entity.Migrations.DbMigrator.ExecuteStatementsInternal(IEnumerable`1 migrationStatements, DbConnection connection)
   場所 System.Data.Entity.Migrations.DbMigrator.<>c__DisplayClass30.<ExecuteStatements>b__2e()
   場所 System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<>c__DisplayClass1.<Execute>b__0()
   場所 System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   場所 System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action operation)
   場所 System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable`1 migrationStatements, DbTransaction existingTransaction)
   場所 System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable`1 migrationStatements)
   場所 System.Data.Entity.Migrations.Infrastructure.MigratorBase.ExecuteStatements(IEnumerable`1 migrationStatements)
   場所 System.Data.Entity.Migrations.DbMigrator.ExecuteOperations(String migrationId, VersionedModel targetModel, IEnumerable`1 operations, IEnumerable`1 systemOperations, Boolean downgrading, Boolean auto)
   場所 System.Data.Entity.Migrations.DbMigrator.ApplyMigration(DbMigration migration, DbMigration lastMigration)
   場所 System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.ApplyMigration(DbMigration migration, DbMigration lastMigration)
   場所 System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   場所 System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   場所 System.Data.Entity.Migrations.DbMigrator.UpdateInternal(String targetMigration)
   場所 System.Data.Entity.Migrations.DbMigrator.<>c__DisplayClassc.<Update>b__b()
   場所 System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
   場所 System.Data.Entity.Migrations.Infrastructure.MigratorBase.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
   場所 System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
   場所 System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)
   場所 System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run()
   場所 System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   場所 System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   場所 System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
   場所 System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration, Boolean force)
   場所 System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0()
   場所 System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
ClientConnectionId:84faae0b-80a8-43e0-a607-a7d1e4940421
Error Number: 5074、State: 1、Class: 16
オブジェクト 'DF__Companies__CityN__5BE2A6F2' は 列 'CityName' に依存しています。
ALTER TABLE ALTER COLUMN CityName は失敗しました。1 つ以上のオブジェクトがこの列を参照しています。

このように制約がSQL Serverにより自動的に設定されているため削除するためにはひと工夫必要になってきます。

今回はStackOverflowを参考にsys.default_constraintsテーブルから対象となる制約名を取得してDropするSQLをラップした拡張メソッドを作成しました。

    public static class MigrationExtensions
    {
        /// <summary>
        /// EFのMigrationで default value 制約を削除するためのpartialメソッド
        /// </summary>
        /// <param name="migration"></param>
        /// <param name="tableName">テーブル名</param>
        /// <param name="colName">列名</param>
        /// <param name="suppressTransaction"></param>
        public static void DeleteDefaultContraint(this IDbMigration migration, string tableName, string colName, bool suppressTransaction = false)
        {
            var sql = new SqlOperation(String.Format(@"DECLARE @SQL varchar(1000)
                SET @SQL='ALTER TABLE {0} DROP CONSTRAINT ['+(SELECT name
                FROM sys.default_constraints
                WHERE parent_object_id = object_id('{0}')
                AND col_name(parent_object_id, parent_column_id) = '{1}')+']';
                PRINT @SQL;
                EXEC(@SQL);", tableName, colName)) { SuppressTransaction = suppressTransaction };
            migration.AddOperation(sql);
        }
    }

利用方法は以下の通りです。

 public override void Up()
        {
...
            this.DeleteDefaultContraint("dbo.GeoMemoes", "ZaitakuFuzai");

            AlterColumn("dbo.GeoMemoes", "ZaitakuFuzai", c => c.Int(nullable: false));
...
        }

これで、制約を削除しやすくなりました。

参考にしたサイト

c# - Code First Migration fails because of a Default Value Constraint - Stack Overflow

OWIN.Environmentでエラーが発生する

こんにちは。beaglesoftの真鍋です。

ASP.NET MVCは気に入っているのですが、初期設定では導入しなかったものを途中から追加する場合には手動で設定する必要が有ることが多いため内容を理解する必要が結構あります。

ASP.NET Identityでエラー

ASP.NET MVC5でASP.NET Identityを利用してログイン処理などを実行した時に次のようなエラーが発生しました。

owin.Environment 項目がコンテキストで見つかりませんでした。

owin.Environment.png (40.9 kB)

このプロジェクトでは、もともと認証を利用しないプロジェクトとして作成後に、ASP.NET Identityによる認証を追加しました。そのため、なにか設定が正しくないだろうということは容易に想像がつきました。

原因について

原因はWeb.configに設定されている内容が正しくなかったことでした。具体的には、以下の設定が行われていたためStartup.csファイルが実行されていなかったためです。

<configuration>
  <appSettings>
    ...
    <add key="owin:AutomaticAppStartup" value="false" />
    ...
  </appSettings>
</configuration>

OWINに関する資料を確認すると、owin:AutomaticAppStartupfalseに設定することでOWINの起動を向こうにすることになります。

To disable OWIN startup discovery add the appSetting owin:AutomaticAppStartup with a value of "false" in the web.config file.

また、規約としてOWINは[AssemblyName].Startupを参照することになっているのですが、プロジェクト名が変更されたときに名前空間が一致していないなどトラブルが発生する予感が満載です。この辺も併せて設定を行いました。

<configuration>
  <appSettings>
    ...
    <add key="owin:appStartup" value="Beaglemap.Startup" />
    <add key="owin:AutomaticAppStartup" value="true" />
    ...
  </appSettings>
</configuration>

"owin:appStartupにOWINの起動時に読み込むファイルを指定することができます。今回は名前空間がBeaglemapでクラス名がStartupなのでBeaglemap.Startupを指定しました。

参考にしたサイト

参考にしたサイトはやはりStackoverflowでこちらに記載のある内容から事象について調査を行いました。 [AssemblyName].Startup

また、具体的な設定については OWIN Startup Class Detection | The ASP.NET Site を確認しました。

ファイルアップロードでのアップロードファイル設定

こんにちは。beaglesoftの真鍋です。

最近はASP.NET MVCとAzureな毎日です。今回はファイルのアップロードに関する設定についてです。

ASP.NET MVCでファイルアップロードでエラー

ASP.NET MVCでファイルアップロードを行うと初期設定の場合には次のようなエラーが表示されることがあります。

screencapture-localhost-59321-photo-upload-1463016344391.png (337.0 kB)

これは、アップロードしたファイルサイズが設定値を超えたために発生するエラーです。

maxRequestLength 省略可能な Int32 型の属性です。 入力ストリームのバッファリングしきい値の限界値を KB 単位で指定します。この限界値は、たとえば大きいファイルをサーバーにポストするユーザーなどにより引き起こされる、サービス拒否攻撃を防止するために使用できます。 既定値は、4096 (4 MB) です。

設定を変更する

設定はWeb.confighttpRuntimeに設定されているmaxRequestLengthを変更します。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    ...
  </configSections>
  <!--
    web.config の変更点の説明については、http://go.microsoft.com/fwlink/?LinkId=235367 を参照してください。

    次の属性を <httpRuntime> タグに設定できます。
      <system.Web>
        <httpRuntime targetFramework="4.6.1" />
      </system.Web>
  -->
  <system.web>
    <authentication mode="None" />
    <compilation debug="true" targetFramework="4.6.1" />
    <!-- ここのhttpRuntimeにmaxRequestLengthを追加してまたは変更して設定する -->
    <httpRuntime targetFramework="4.5.2" maxRequestLength="10240" />
    <httpModules>
      <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
    </httpModules>
  </system.web>

...
</configuration>

注意点

httpRuntimesystem.web内に1つしか設定できないためすでに設定がある場合にはその設定に追記するようになります。

参考

httpRuntime 要素 (ASP.NET 設定スキーマ).aspx)

たいてい新しいプロジェクトで開発をすると引っかかるのでメモとして残しておこうと思います。