プロバイダーが提供するSDKを使用して、簡単に統合できるアプリケーションがあります。このSDKはAMQPエンドポイントに接続し、メッセージをコンシューマーに配信、キャッシュ、および変換するだけです。以前、この統合はデータソースとしてXMLを使用するHTTP経由で行われ、古い統合には、WebリクエストごととマネージスレッドIDごとの2つのDataContextキャッシュ方法がありました。(1)
ただし、SDKがすべての接続ロジックを実行し、コンシューマーを定義するだけなので、HTTP経由ではなく、AMQPが透過的になり、「Webリクエストごとに」DataContextをキャッシュするオプションがないため、管理スレッドごとにのみidが残されます。責任パターンのチェーンを実装したので、更新が来ると、DataContextを使用して新しい更新に従ってデータベースを更新するハンドラーの1つのパイプラインに配置されます。パイプラインの呼び出し方法は次のようになります。
public Task Invoke(TInput entity)
{
object currentInputArgument = entity;
for (var i = 0; i < _pipeline.Count; ++i)
{
var action = _pipeline[i];
if (action.Method.ReturnType.IsSubclassOf(typeof(Task)))
{
if (action.Method.ReturnType.IsConstructedGenericType)
{
dynamic tmp = action.DynamicInvoke(currentInputArgument);
currentInputArgument = tmp.GetAwaiter().GetResult();
}
else
{
(action.DynamicInvoke(currentInputArgument) as Task).GetAwaiter().GetResult();
}
}
else
{
currentInputArgument = action.DynamicInvoke(currentInputArgument);
}
}
return Task.CompletedTask;
}
問題は、(少なくとも私が思うに)この責任の連鎖は、新しいタスクを返す/開始するメソッドの連鎖であるため、エンティティAの更新が発生すると、マネージスレッドid = 1によって処理され、その後しばらくの間ここでも、同じエンティティAが管理対象スレッドid = 2によって処理されるためだけに到着します。これはにつながります:
System.InvalidOperationException: 'エンティティオブジェクトはIEntityChangeTrackerの複数のインスタンスから参照できません。
マネージスレッドid = 1からのDataContextは既にエンティティAを追跡しているためです(少なくとも、それは私が思うとおりです)。
私の質問は、私の場合にどのようにDataContextをキャッシュできるかです。君たちは同じ問題を抱えていましたか?私はこれを読んで、この答えと、1つの静的DataContextを使用して理解したことからも、オプションではありません。(2)
- 免責事項:私たちはアプリケーションを継承したと言っておくべきでしたが、なぜそのように実装されたのか答えることはできません。
- 免責事項2:EFの経験はほとんどありません。
コミュニティからの質問:
- 使用しているEFのバージョンは何ですか?5.0
- エンティティがコンテキストよりも長生きするのはなぜですか?-そうではありませんが、エンティティがコンテキストよりも長く生きる必要がある理由を尋ねているのかもしれません。私は、キャッシュされたDataContextを使用するリポジトリを使用して、データベースからエンティティを取得し、キャッシュとして使用するメモリ内コレクションに格納します。
これは、エンティティが「抽出」される方法です。ここDatabaseDataContext
で、私が話しているキャッシュされたDataContext はどこにありますか(データベースセット全体が含まれるBLOB)
protected IQueryable<T> Get<TProperty>(params Expression<Func<T, TProperty>>[] includes)
{
var query = DatabaseDataContext.Set<T>().AsQueryable();
if (includes != null && includes.Length > 0)
{
foreach (var item in includes)
{
query = query.Include(item);
}
}
return query;
}
次に、私のコンシューマーアプリケーションがAMQPメッセージを受信するたびに、責任パターンのチェーンが、このメッセージとそのデータがすでに処理されているかどうかのチェックを開始します。だから私はそのようなメソッドがあります:
public async Task<TEntity> Handle<TEntity>(TEntity sportEvent)
where TEntity : ISportEvent
{
... some unimportant business logic
//save the sport
if (sport.SportID > 0) // <-- this here basically checks if so called
// sport is found in cache or not
// if its found then we update the entity in the db
// and update the cache after that
{
_sportRepository.Update(sport); /*
* because message update for the same sport can come
* and since DataContext is cached by threadId like I said
* and Update can be executed from different threads
* this is where aforementioned exception is thrown
*/
}
else // if not simply insert the entity in the db and the caches
{
_sportRepository.Insert(sport);
}
_sportRepository.SaveDbChanges();
... updating caches logic
}
AsNoTracking()
メソッドを使用してデータベースからエンティティを取得するか、エンティティを「更新」または「挿入」するたびにエンティティをデタッチするとこれを解決できると思っていましたが、解決しませんでした。
SaveChanges
ます。