あなたが知らないかもしれないEntity Framework 6の拡匵







倚くのプログラマヌは、メモを䜜成し、困難を説明し、自分の矩務のために察凊しなければならない矎しくおそれほど難しくない決定をしたす。 それはあなた自身の技術的なブログ、働くwiki、たたは通垞のノヌトブックでさえありえたす-本質は䞀぀です。 埐々に、小さなEvernoteから、Habrの蚘事党䜓が成長する可胜性があるず指摘されおいたす。 しかし、時間が経ち、仕事の倉曎が開発スタックの倉曎を玄束し、技術はただ止たりたせんずころで、 バヌゞョン1.1のように、EF Coreは数か月間存圚しおいたした。 䞀方、Entity Framework 6は、特にその安定性、䜎いしきい倀、幅広い人気により、.netスタック䞊の䌁業アプリケヌションのデヌタにアクセスするための䞻力補品であり続けおいたす。 したがっお、この蚘事がただ誰かに圹立぀こずを願っおいたす。







内容







  1. EDMXなしのデヌタベヌスファヌスト
  2. 切断されたグラフを操䜜する
  3. SQLの倉曎。 テヌブルの方向を远加する
  4. DbContextラむフタむムキャッシング
  5. SQL Serverからの゚ラヌを再詊行する
  6. DbContextを眮き換え、実際のデヌタベヌスから分離
  7. クむック挿入


EDMXなしのデヌタベヌスファヌスト



「コヌドファヌストずデヌタベヌスファヌスト」の議論の次のラりンドを開始したくありたせん。 Database Firstを奜むなら、あなたの人生をもっず楜にする方法を教えたほうがいいでしょう。 このアプロヌチを䜿甚する倚くの開発者は、かさばるEDMXファむルで䜜業するこずの䞍䟿さに泚目しおいたす。 このファむルは、チヌム開発を地獄に倉え、その内郚構造の絶え間ない「混合」のために䞊行倉曎のマヌゞを非垞に耇雑にしたす。 数癟の゚ンティティ通垞のレガシヌモノリスなどを含むモデルでは、他の欠点の䞭でも、暙準のEDMXデザむナヌで䜜業する堎合、アクションの速床が倧幅に䜎䞋する堎合がありたす。







゜リュヌションは明らかなようです-POCOを生成しおメタデヌタを保存する代替手段を支持しお、EDMXを攟棄する必芁がありたす。 タスクは難しくなく、EFには「すぐに䜿甚可胜」なものがありたす。これは、Visual Studioで利甚可胜な「デヌタベヌスからコヌドを最初に生成する」アむテムですVS2015。 ただし、実際には、このツヌルを䜿甚しおデヌタベヌスの倉曎を結果のモデルに反映するこずは非垞に䞍䟿です。 さらに、EFで長い間働いおきた人は、同様の問題を解決するEntity Framework Power Tools拡匵機胜を芚えおいるかもしれたせんが、残念ながら、プロゞェクトはもう開発されおおらずVS2015をハックせずにむンストヌルするこずはできたせん、このツヌルの䞀郚の開発者はEFチヌムで盎接䜜業しおいたす







すべおが悪いように思えたす。ここで、 EntityFramework Reverse POCO Generatorを芋぀けたした。 これは、倚数の蚭定ずオヌプン゜ヌスを持぀既存のデヌタベヌスに基づいおPOCOを生成するためのT4テンプレヌトです。 すべおの䞻芁なEDMX機胜がサポヌトされおおり、ナニットテスト甚のFakeDbContext / FakeDbSetの生成、属性䟋DataContract / DataMemberなどのモデルのカバヌなど、倚くの远加機胜がありたす。T4のおかげで、コヌド生成を完党に制埡できたす。 芁玄安定しお動䜜し、チヌムは気に入っおいたす。既存のプロゞェクトを簡単に移行できたす。







切断されたグラフを操䜜する



通垞、新しいコンテキストたたは以前に取埗した異なるコンテキストの単䞀オブゞェクトをDbContextにアタッチするこずは難しくありたせん。 問題は、グラフの堎合に始たりたす。 リンクのある゚ンティティ-EF "out of the box"は、゚ンティティのナビゲヌションプロパティのコンテンツの倉曎を远跡せず、コンテキストに再結合したす。 コンテキストの存続期間䞭、゚ンティティオブゞェクトごずに倉曎を远跡するには、察応する゚ントリ゚ンティティの状態远加、倉曎、削陀などを含むサヌビス情報を持぀オブゞェクトが存圚する必芁がありたす。 グラフに参加するための゚ントリを入力したす-少なくずも2぀の方法が可胜です。







  1. ゚ンティティ自䜓に状態を保存し、倉曎を独自に远跡したす。 したがっお、切断されたグラフには、参加に必芁なすべおの情報が含たれたす。
  2. 事前に䜕もせずに、新しいコンテキストにグラフを远加するずきに、デヌタベヌスから元のグラフを取り出し、2぀のグラフの比范に基づいお゚ントリ状態を配眮したす。


゜リュヌション1の䟋は、たずえば、有名なEFスペシャリストであるゞュリヌラヌマンの最新のPluralsightコヌスにありたす。 独立した汎甚実装には、倚数のゞェスチャが必芁です。 すべおの゚ンティティは、IStateObjectむンタヌフェむスを実装する必芁がありたす。







public interface IStateObject { ObjectState State { get; set; } }
      
      





䜕らかの方法で、グラフ内の各゚ンティティをコンテキストに手動で添付した埌、状態倀の関連性を確認する必芁がありたす。







 context.Foos.Attach(foo);
      
      





すべおの゚ントリを調べお、状態を線集したす。







 IStateObject entity = entry.Entity; entry.State = ConvertState(entity.State);
      
      





この堎合、デヌタベヌスぞの远加の呌び出しは必芁ありたせんが、゜リュヌションは膚倧で壊れやすく、倚察倚の関係では動䜜しない可胜性がありたす。 モデルも散らばっおいたしたずころで、むンタヌフェヌスの実装芁件は、蚘事の前のセクションのT4テンプレヌトを倉曎するこずで解決できたす。







解決策2を怜蚎しおください。 簡単に説明したす。







 context.UpdateGraph(root, map => map.OwnedCollection(r => r.Childs));
      
      





これを呌び出す-ルヌト゚ンティティをコンテキストに远加し、Children子オブゞェクトのコレクションでナビゲヌションプロパティを曎新したすが、デヌタベヌスぞのSELECTのみのコストがかかりたす。 GraphDiffラむブラリのおかげで可胜になったのは、䜜者がすべおの汚い仕事をし、䞻芁なバグをキャッチしたからです。







SQLの倉曎。 テヌブルの方向を远加する



䞀芋単玔なSELECT ... FROM Table WITHUPDLOCKステヌトメントの生成は、EFではサポヌトされおいたせん。 ただし、たずえば正芏衚珟を䜿甚するなど、適切な方法で生成されたSQLを倉曎できるむンタヌセプタヌがありたす。 コンテキストの有効期間内に生成された各SELECTにUPDLOCKを远加したしょう圓然、粒床は必ずしもコンテキストではなく、すべお実装に䟝存したす。







 using (var ctx = new MyDbContext().With(SqlLockMode.UpdLock)) {}
      
      





これを行うには、コンテキスト内でWithメ゜ッドを宣蚀し、むンタヌセプタヌを登録したす。







 public interface ILockContext { SqlLockMode LockMode { get; set; } MyDbContext With(SqlLockMode lockMode); } public class MyDbConfig : DbConfiguration { public MyDbConfig() { AddInterceptor(new LockInterceptor()); } } [DbConfigurationType(typeof(MyDbConfig))] public partial class MyDbContext : ILockContext { public SqlLockMode LockMode { get; set; } public MyDbContext With(SqlLockMode lockMode) { LockMode = lockMode; return this; } private static void MyDbContextStaticPartial() { } }
      
      





ロックむンタヌセプタヌ
 public class LockInterceptor : DbCommandInterceptor { public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext) { AddLockStatement(command, interceptionContext); } public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { AddLockStatement(command, interceptionContext); } private void AddLockStatement<T>(DbCommand command, DbCommandInterceptionContext<T> interceptionContext) { var lockMode = GetLock(interceptionContext); switch (lockMode) { case SqlLockMode.UpdLock: command.CommandText = SqlModifier.AddUpdLock(command.CommandText); break; } } private SqlLockMode GetLock<T>(DbCommandInterceptionContext<T> interceptionContext) { if (interceptionContext == null) return SqlLockMode.None; ILockContext lockContext = interceptionContext.DbContexts.First() as ILockContext; if (lockContext == null) return SqlLockMode.None; return lockContext.LockMode; } }
      
      





正芏衚珟は次のようになりたす。
 public static class SqlModifier { private static readonly Regex _regex = new Regex(@"(?<tableAlias>SELECT\s.*FROM\s.*AS \[Extent\d+])", RegexOptions.Multiline | RegexOptions.IgnoreCase); public static string AddUpdLock(string text) { return _regex.Replace(text, "${tableAlias} WITH (UPDLOCK)"); } }
      
      





正芏衚珟のテスト
 public class SqlModifier_Tests { [TestCase("SELECT [Extent1].[Name] AS [Name] FROM [dbo].[Customer] AS [Extent1]")] [TestCase("SELECT * FROM [dbo].[Customer] AS [Extent999]")] public void AddUpdLock_ValidEfSelectStatement_AddLockAfterTableAlias(string text) { string expected = text + " WITH (UPDLOCK)"; string actual = SqlModifier.AddUpdLock(text); Assert.AreEqual(expected, actual); } [TestCase("SELECT [Extent1].[Extent1] AS [Extent1]")] [TestCase("SELECT * FROM Order")] [TestCase(" AS [Extent111]")] public void AddUpdLock_InvalidEfSelectStatement_NoChange(string text) { string actual = SqlModifier.AddUpdLock(text); Assert.AreEqual(text, actual); } }
      
      





DbContextラむフタむムキャッシング



EFは次のようなものをキャッシュしたす。









デヌタのキャッシングは人生のコンテキスト内でのみ行われFindメ゜ッドを思い出しおください、この蚀語を本栌的なキャッシュず呌ぶこずはできたせん。 プロセスメモリ内のすべおのコンテキストキャッシュ甚に制埡された統䞀されたキャッシュをどのように敎理したすか EntityFramework.Plusたたはその「貧匱な」代替EntityFramework.Cacheを䜿甚したす。







 public void SelectWithCache() { using (var ctx = new MyDbContext()) { ctx.Customers.FromCache().ToList(); } } [Test] public void SelectWithCache_Test() { TimeSpan expiration = TimeSpan.FromSeconds(5); var options = new CacheItemPolicy() { SlidingExpiration = expiration }; QueryCacheManager.DefaultCacheItemPolicy = options; SelectWithCache(); //   SelectWithCache(); //  Thread.Sleep(expiration); SelectWithCache(); //   }
      
      





SelectWithCacheの2回目の呌び出しがデヌタベヌスに圱響しないこずを確認するには、SQLプロファむラヌを実行するだけで十分です。 遅延ヒットもキャッシュされたす。







さらに、EFず分散キャッシュの統合が可胜です。 たずえば、EntityFramework.Plusに接続されたSytem.Runtime.Caching.ObjectCacheに基づく自己蚘述型のキャッシュマネヌゞャヌを䜿甚したす。 NCache は 、すぐに䜿甚できるEFずの統合をサポヌトしおいたす詳现を共有できたせん-このキャッシュは詊しおいたせん。







SQL Serverからの゚ラヌを再詊行する



 public class SchoolConfiguration : DbConfiguration { public SchoolConfiguration() { SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy(maxRetryCount: 3, maxDelay: TimeSpan.FromSeconds(10))); } }
      
      





SqlAzureExecutionStrategy-この戊略はEF6に含たれおいたす既定では無効。 䜿甚する堎合、SQL Serverからの応答で特定の゚ラヌコヌドを受信するず、サヌバヌにSQLステヌトメントが繰り返し送信されたす。







SqlAzureExecutionStrategyの゚ラヌコヌド
 // SQL Error Code: 40197 // The service has encountered an error processing your request. Please try again. case 40197: // SQL Error Code: 40501 // The service is currently busy. Retry the request after 10 seconds. case 40501: // SQL Error Code: 10053 // A transport-level error has occurred when receiving results from the server. // An established connection was aborted by the software in your host machine. case 10053: // SQL Error Code: 10054 // A transport-level error has occurred when sending the request to the server. // (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.) case 10054: // SQL Error Code: 10060 // A network-related or instance-specific error occurred while establishing a connection to SQL Server. // The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server // is configured to allow remote connections. (provider: TCP Provider, error: 0 - A connection attempt failed // because the connected party did not properly respond after a period of time, or established connection failed // because connected host has failed to respond.)"} case 10060: // SQL Error Code: 40613 // Database XXXX on server YYYY is not currently available. Please retry the connection later. If the problem persists, contact customer // support, and provide them the session tracing ID of ZZZZZ. case 40613: // SQL Error Code: 40143 // The service has encountered an error processing your request. Please try again. case 40143: // SQL Error Code: 233 // The client was unable to establish a connection because of an error during connection initialization process before login. // Possible causes include the following: the client tried to connect to an unsupported version of SQL Server; the server was too busy // to accept new connections; or there was a resource limitation (insufficient memory or maximum allowed connections) on the server. // (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.) case 233: // SQL Error Code: 64 // A connection was successfully established with the server, but then an error occurred during the login process. // (provider: TCP Provider, error: 0 - The specified network name is no longer available.) case 64: // DBNETLIB Error Code: 20 // The instance of SQL Server you attempted to connect to does not support encryption. case (int)ProcessNetLibErrorCode.EncryptionNotSupported: return true;
      
      





興味









DbContextを眮き換え、実際のデヌタベヌスから分離



テストのために、呌び出しコヌドのDbContextを透過的に眮き換え、停のDbSetにテストデヌタを入力したす。 問題を解決する方法をいく぀か玹介したす。

方法1 長いIMyDbContextおよびDbSetのスタブを手動で䜜成し、必芁な動䜜を明瀺的に指定したす。 Moqラむブラリを䜿甚するず、次のようになりたす。





モックデヌタベヌス
 public IMyDbContext Create() { var mockRepository = new MockRepository(MockBehavior.Default); var mockContext = mockRepository.Create<IMyDbContext>(); mockContext.Setup(x => x.SaveChanges()).Returns(int.MaxValue); var mockDbSet = MockDbSet<Customer>(customers); mockContext.Setup(m => m.Customers).Returns(mockDbSet.Object); return mockContext.Object; } private Mock<DbSet<T>> MockDbSet<T>(List<T> data = null) where T : class { if (data == null) data = new List<T>(); var queryable = data.AsQueryable(); var mock = new Mock<DbSet<T>>(); mock.As<IQueryable<T>>().Setup(m => m.Provider) .Returns(queryable.Provider); mock.As<IQueryable<T>>().Setup(m => m.Expression) .Returns(queryable.Expression); mock.As<IQueryable<T>>().Setup(m => m.ElementType) .Returns(queryable.ElementType); mock.As<IQueryable<T>>().Setup(m => m.GetEnumerator()) .Returns(queryable.GetEnumerator()); return mock; }
      
      





MSDNに関するこのトピックに関する基本的な蚘事がありたす モックフレヌムワヌクを䜿甚したEntity Framework TestingEF6以降 。 そしお、このアプロヌチはか぀お非垞に印象的で、githubでデモプロゞェクトを取埗したしたEF6 DbFirst、SQL Server、Moq、Ninjectを䜿甚。 ちなみに、 ゚ンタヌプラむズコヌスで既に説明したEntity Frameworkのテストに぀いおは、章党䜓で説明したす。







方法2 短い既に説明したReverse POCO Generatorを䜿甚したす。これは、デフォルトでDbContextおよびすべおのDbSetのスタブを䜜成したすFakeDbSet内には通垞のメモリ内コレクションがありたす。







クむック挿入



数千の新しいレコヌドをSQL Serverデヌタベヌスに同時に挿入するには、暙準の行ごずのINSERTの代わりにBULK操䜜を䜿甚するのが非垞に効率的です。 この問題に぀いおは別の蚘事で詳しく説明したした。したがっお、以䞋ではSqlBulkCopyに基づくすぐに䜿甚できる゜リュヌションのみを提䟛したす。







→ EntityFramework.Utilities

→ ゚ンティティフレヌムワヌク拡匵







それだけです。 コメントでレシピやコツを共有しおください=
















All Articles