تزامن ومكتبة الدولة لـ .NET ، تشرق في مكالمات الشبكة.
حزمة nuget متوفرة على https://www.nuget.org/packages/serviceConcurrency/
مكتبة للتعامل مع التزامن والدولة ، بشكل أساسي للرمز الذي يستدعي خدمات الويب.
أولاً ، يمنع حدوث مكالمات غير ضرورية. عندما تكون مكالمة مع حجج مطابقة بالفعل في رحلة ، يكون المتصل المتزامن متوقفًا وسيستأنف عند الانتهاء من الطلب الأصلي. إذا كانت الحجة عبارة عن مجموعة ، فسيتم تجريد الكيانات الموجودة بالفعل.
ثانياً ، يقوم بتخزين حالة عودة القيمة. عندما توجد قيمة مخزنة مؤقتًا لأي طلب معين ، سيتم إرجاعها بدلاً من إجراء مكالمة. يمكن الوصول إلى هذه القيمة في أي وقت ، مما يمنع الحاجة إلى حقول دعم إضافية في الكود الخاص بك. إذا كانت الحجة عبارة عن مجموعة ، يتم تجريد الكيانات المخزنة مؤقتًا من الحجة.
تستخدم هذه المكتبة imemorycache للتخزين المؤقت للبيانات الداخلية.
إليك بعض الأشياء التي يمكن لـ ServiceConcurrency التعامل معها لك. يمكن العثور على المزيد من الأمثلة المتعمقة في أقسام لاحقة من هذا المستند.
ستعيد GetSessionToken () قيمة مخزنة مؤقتًا بعد إجراء مكالمة أولى. إذا تم استدعاء GetSessionToken () بشكل متزامن في حين أنه لا يتمتع بعد بالتخزين مؤقتًا ، فسيتم إجراء مكالمة واحدة فقط لـ FetchSessionToken () وسيتم تسليم المكالمات المتزامنة الأخرى حتى تتوفر قيمة.
private ServiceConcurrency . ReturnsValue < string > sessionTokenState =
new ServiceConcurrency . ReturnsValue < string > ( ) ;
public async Task < string > GetSessionToken ( )
{
return await this . sessionTokenState . Execute ( this . FetchSessionToken ) ;
}كما هو مذكور أعلاه ، ولكن تم تجريد مجموعة الحجج من القيم التي تم تخزينها بالفعل أو أثناء الطيران. لذلك لن يتم استدعاء FetchuserProfiles () مع معرف أكثر من مرة.
private ServiceConcurrency . TakesEnumerationArgReturnsValue < Guid , UserProfile > userProfilesState =
new ServiceConcurrency . TakesEnumerationArgReturnsValue < Guid , UserProfile > ( ) ;
public async Task < IEnumerable < UserProfile > > GetUserProfiles ( IEnumerable < Guid > userProfileIds )
{
return await this . userProfilesState . Execute (
this . FetchUserProfiles ,
( guid , results ) => results . SingleOrDefault ( t => t . Id == guid ) ,
userProfileIds
) ;
} هناك أربعة فصول في هذه المكتبة:
noargnovalue
يمنع المكالمات المتزامنة من خلال السماح فقط بالاتصال النشط في وقت واحد ، حيث تنتظر المكالمات المتزامنة حتى الانتهاء من المكالمة النشطة.
ReturnsValue <TValue>
سيتم إجراء مكالمة واحدة فقط وستجلب المكالمات اللاحقة القيمة من ذاكرة التخزين المؤقت. يمنع أيضًا حدوث أي مكالمات متزامنة ، من خلال السماح فقط بالاتصال النشط في وقت واحد ، حيث تنتظر المكالمات المتزامنة حتى الانتهاء من المكالمة النشطة.
Takesarg <Targ>
يمنع المكالمات المتزامنة عند مشاركة نفس الوسيطة ، من خلال السماح فقط بالاتصال النشط في وقت واحد لكل وسيطة ، حيث ستنتظر المكالمات المتزامنة أن تنتهي المكالمة النشطة.
TakesArgreturnsvalue <Targ ، tvalue>
للحصول على حجة معينة ، سيتم إجراء مكالمة واحدة فقط وسيجلب المكالمات اللاحقة القيمة من ذاكرة التخزين المؤقت. يمنع أيضًا أي مكالمات متزامنة من حدوثها ، من خلال السماح فقط بالاتصال النشط في وقت واحد لكل وسيطة ، حيث تنتظر المكالمات المتزامنة حتى الانتهاء من المكالمة النشطة.
takesenumerationArg <Targ>
سيتم تنفيذ المكالمات المتزامنة مرة واحدة فقط للحصول على حجة معينة في مجموعة الوسيطة.
يتم تجريد مجموعة الحجة من القيم التي تكون العملية بالفعل في رحلة.
في نفس الوقت ، سوف يؤدي الاتصال بـ ["A" ، "B" ، "C"] و ["B" ، "C" ، "D"] إلى دعوة واحدة مع ["A" ، "B" ، "C"] وواحد مع ["D"].
TakeenumerAmentArgreturnSvalue <Targ ، tvalue>
ستنفذ المكالمة الأولى ، وأي مكالمات متزامنة ، مرة واحدة فقط للحصول على وسيطة معينة في مجموعة الوسيطة. ستجلب المكالمات اللاحقة قيمة من ذاكرة التخزين المؤقت.
يتم تجريد مجموعة الوسيطة من القيم التي توجد بها قيمة مخزنة مؤقتًا أو تعمل بالفعل في رحلة.
في نفس الوقت ، سوف يؤدي الاتصال بـ ["A" ، "B" ، "C"] و ["B" ، "C" ، "D"] إلى دعوة واحدة مع ["A" ، "B" ، "C"] وواحد مع ["D"]. في المرة التالية "A" أو "B" أو "C" أو "D" ، سيتم تجريدها من المجموعة وسيتم جلبها من ذاكرة التخزين المؤقت.
تحتوي جميع كائنات ServiceConcurrency على مُنشئ بدون معلمة ، وفي هذه الحالة سيتم إنشاء ImemoryCache الداخلي.
تحتوي جميع كائنات ServiceConcurrency التي ترجع أيضًا على مُنشئ يقبل كائن ImemoryCache ، في حالة رغبتك في مشاركة ذاكرة التخزين المؤقت مع جميع كائنات ServiceConcurrency. إذا قمت بذلك ، يرجى التأكد من استخدام مفاتيح فريدة من نوعها على الإدخالات.
T Execute ( .. . )ينفذ طلب محدد. يمكنك تقديم رد اتصال لإجراء المكالمة الخارجية. راجع الأمثلة لمزيد من المعلومات ، لأن الوسائط وأنواع الإرجاع هي نوع العملات المحددة. القيمة الناتجة مخزنة مؤقتًا وتقديمها لك في رد الاتصال.
void Reset ( )أعد تعيين الحالة الداخلية ، IE للمكالمات في العملية وذاكرة التخزين المؤقت الداخلية (في حالة قيام كائن ServiceConcurrency بإرجاع القيم).
bool IsExecuting ( )إرجاع ما إذا كان كائن ServiceConCurrency يقوم حاليًا بتنفيذ طلب محدد.
الأساليب التالية متوفرة فقط في كائنات ServiceConCurrency التي تُرجع قيمًا.
void ResetCache ( )إعادة تعيين ذاكرة التخزين المؤقت الداخلية. ودعا أيضا من إعادة تعيين ().
bool TryGetValue ( TArg key , out TValue value )يحصل على قيمة من ذاكرة التخزين المؤقت.
void Set ( TArg key , TValue value )ضابط ذاكرة التخزين المؤقت في الحالة النادرة التي قد تحتاج إلى معالجة ذاكرة التخزين المؤقت يدويًا.
void Remove ( TArg key )يزيل قيمة من ذاكرة التخزين المؤقت.
bool ContainsKey ( TArg key )يتحقق ما إذا كان هناك إدخال في ذاكرة التخزين المؤقت.
TValue this [ TArg key ]مشغل صفيف لإعداد إدخالات ذاكرة التخزين المؤقت. يلقي keynotfoundException في getter إذا لم يكن هناك إدخال.
IEnumerator < KeyValuePair < TArg , TValue > > GetEnumerator ( )
IEnumerator IEnumerable . GetEnumerator ( )العدادات لذاكرة التخزين المؤقت الداخلية. يتيح لك استخدام الكائنات في عبارات Foreach و LINQ.
void Dispose ( ) التخلص من ذاكرة التخزين المؤقت الداخلية في حالة Bool IsCacheShared كاذبة. إذا كانت ذاكرة التخزين المؤقت المشتركة ، فستستدعي ResetCache بدلاً من ذلك.
TValue Value ;فقط في ReturnsValue <TValue>. هذا هو الكائن المخطط مؤقتًا.
الخصائص التالية متوفرة فقط في كائنات ServiceConcurrency التي تُرجع قيمًا.
MemoryCacheEntryOptions CacheEntryOptions ;يتم استخدام هذه الخيارات داخليًا عند تخزين القيمة مؤقتًا. يتيح لك تحريرها ضبط انتهاء الصلاحية ، وأحجام ذاكرة التخزين المؤقت ، وما إلى ذلك ، راجع MemoryCacheOptions لمزيد من التفاصيل.
bool IsCacheShared ;getter فقط. يدل على ما إذا كانت ذاكرة التخزين المؤقت مشاركة أم لا.
يقبل Execute () محول قيمة اختياري ، والذي يمكنه تعديل القيمة المحددة قبل إرجاعها وتخزينها. هذا متاح فقط في كائنات ServiceConcurrency التي تُرجع قيمًا.
private ServiceConcurrency . ReturnsValue < string > lastName =
new ServiceConcurrency . ReturnsValue < string > ( ) ;
private const string FirstName = "John" ;
public async Task < string > GetFullName ( )
{
return await this . lastName . Execute (
this . GetLastName ,
( lastName ) => $ " { FirstName } { lastName } " ;
) ;
}تقبل كائنات ServiceConCurrency أيضًا معلمة إضافية تعلن نوع قيمة الطلب الداخلي. عند تحديد ذلك ، سيتم تحويل محول القيمة أيضًا بين نوع المصدر ونوع الوجهة. يكون هذا مفيدًا إذا كان الطلب الذي تم استدعاؤه بواسطة Execute () من نوع مختلف عن حقل الدعم المطلوب.
// FetchChatRooms() returns an IEnumerable<ChatRoom>, chatRoomMap handles it as Dictionary<Guid, ChatRoom>
private ServiceConcurrency . ReturnsValue < IEnumerable < ChatRoom > , Dictionary < Guid , ChatRoom > > chatRoomMap =
new ServiceConcurrency . ReturnsValue < IEnumerable < ChatRoom > , Dictionary < Guid , ChatRoom > > ( ) ;
public async Task < IEnumerable < ChatRoom > > UpdateChatRooms ( )
{
return ( await this . chatRoomMap . Execute (
this . FetchChatRooms ,
( chatRooms ) => chatRooms . ToDictionary ( t => t . Id , t => t ) // cache as id -> chatroom map
) ) ? . Values ;
}
public ChatRoom GetChatRoom ( Guid chatRoomId )
{
ChatRoom chatRoom ;
if ( this . chatRoomMap . Value . TryGetValue ( chatRoomId , out chatRoom ) ) // value is Dictionary<Guid, ChatRoom>
return chatRoom ;
return null ;
} using System ;
using System . Collections . Generic ;
using System . Threading . Tasks ;
using System . Linq ;
public class MyService : IDisposable
{
////////////////////////////////////////////////////////////////////////////
// NoArgNoValue example
private ServiceConcurrency . NoArgNoValue simpleCallState =
new ServiceConcurrency . NoArgNoValue ( ) ;
// Concurrent calls won't invoke the callback multiple times - only the first
// call will invoke it, and the rest will wait until it finishes.
public async Task CallSomething ( )
{
await this . simpleCallState . Execute (
async ( ) =>
{
Console . WriteLine ( "CallSomething call in flight" ) ;
await Task . Delay ( 100 ) ;
}
) ;
}
////////////////////////////////////////////////////////////////////////////
// ReturnsValue example
private ServiceConcurrency . ReturnsValue < string > returnsValueState =
new ServiceConcurrency . ReturnsValue < string > ( ) ;
// Only one call will be made and subsequent calls will fetch the value from
// cache. Also prevents any concurrent calls from occurring, by allowing only
// one active call at a time, where concurrent calls will wait for the active
// call to finish.
public async Task < string > FetchSomething ( )
{
return await this . returnsValueState . Execute (
async ( ) =>
{
Console . WriteLine ( "FetchSomething call in flight" ) ;
await Task . Delay ( 100 ) ;
return "Hello world!" ;
}
) ;
}
////////////////////////////////////////////////////////////////////////////
// TakesArg example
private ServiceConcurrency . TakesArg < Guid > takesArgState =
new ServiceConcurrency . TakesArg < Guid > ( ) ;
// Prevents concurrent calls when sharing the same argument, by allowing only
// one active call at a time per argument, where concurrent calls will wait for
// the active call to finish.
public async Task PostSomething ( Guid someId )
{
await this . takesArgState . Execute (
async ( Guid id ) =>
{
Console . WriteLine ( $ "PostSomething call in flight, for argument { id } " ) ;
await Task . Delay ( 100 ) ;
} ,
someId
) ;
}
////////////////////////////////////////////////////////////////////////////
// TakesArgReturnsValue example
private ServiceConcurrency . TakesArgReturnsValue < Guid , string > takesArgReturnsValueState =
new ServiceConcurrency . TakesArgReturnsValue < Guid , string > ( ) ;
// For a given argument, only one call will be made and subsequent calls will
// fetch the value from cache. Also prevents any concurrent calls from occurring,
// by allowing only one active call at a time per argument, where concurrent
// calls will wait for the active call to finish.
public async Task < string > FetchSomethingFor ( Guid someId )
{
return await this . takesArgReturnsValueState . Execute (
async ( Guid id ) =>
{
Console . WriteLine ( $ "FetchSomethingFor call in flight, for argument { id } " ) ;
await Task . Delay ( 100 ) ;
return $ "The guid is { id } " ;
} ,
someId
) ;
}
////////////////////////////////////////////////////////////////////////////
// TakesEnumerationArg example
private ServiceConcurrency . TakesEnumerationArg < Guid > takesEnumerationArgState =
new ServiceConcurrency . TakesEnumerationArg < Guid > ( ) ;
// Concurrent calls will execute only once for a given argument in the argument
// collection.
//
// The argument collection is stripped of values for which an operation is already
// in flight.
//
// So simultaneously calling with ["A", "B", "C"] and ["B", "C", "D"] will result
// in one call with ["A", "B", "C"] and one with ["D"].
public async Task PostCollection ( IEnumerable < Guid > someIds )
{
await this . takesEnumerationArgState . Execute (
async ( IEnumerable < Guid > ids ) =>
{
Console . WriteLine ( $ "PostCollection call in flight, for arguments { ids . Select ( t => t ) } " ) ;
await Task . Delay ( 100 ) ;
} ,
someIds
) ;
}
////////////////////////////////////////////////////////////////////////////
// TakesEnumerationArgReturnsValue example
private ServiceConcurrency . TakesEnumerationArgReturnsValue < Guid , ExampleClass > takesEnumArgReturnsValueState =
new ServiceConcurrency . TakesEnumerationArgReturnsValue < Guid , ExampleClass > ( ) ;
public class ExampleClass
{
public Guid Id { get ; set ; }
}
// The first call, and any concurrent calls, will execute only once for a
// given argument in the argument collection. Subsequent calls will fetch value
// from cache.
//
// The argument collection is stripped of values for which a cached value exists
// or an operation is alraedy in flight.
//
// So simultaneously calling with ["A", "B", "C"] and ["B", "C", "D"] will
// result in one call with ["A", "B", "C"] and one with ["D"].
// The next time "A", "B", "C" or "D" is called with, it will be stripped from
// the collection and a value for it will be fetched from the cache.
public async Task < IEnumerable < ExampleClass > > FetchCollectionForThese ( IEnumerable < Guid > someIds )
{
return await this . takesEnumArgReturnsValueState . Execute (
async ( IEnumerable < Guid > ids ) =>
{
Console . WriteLine ( $ "FetchCollectionForThese call in flight, for arguments { ids . Select ( t => t ) } " ) ;
await Task . Delay ( 100 ) ;
return ids . Select ( t => new ExampleClass ( )
{
Id = t
} ) ;
} ,
// a mapper from arg to result is required - should return the corresponding value for the passed argument
( Guid id , IEnumerable < ExampleClass > result ) => result . SingleOrDefault ( t => t . Id == id ) ,
someIds
) ;
}
void Dispose ( )
{
this . simpleCallState . Dispose ( ) ;
this . returnsValueState . Dispose ( ) ;
this . takesArgState . Dispose ( ) ;
this . takesArgReturnsValueState . Dispose ( ) ;
this . takesEnumerationArgState . Dispose ( ) ;
this . takesEnumArgReturnsValueState . Dispose ( ) ;
}
}