ห้องสมุดที่เกิดขึ้นพร้อมกันและรัฐสำหรับ. 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 () จะไม่ถูกเรียกด้วย ID มากกว่าหนึ่งครั้ง
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 <Value>
จะมีการโทรเพียงครั้งเดียวและการโทรที่ตามมาจะดึงค่าจากแคช นอกจากนี้ยังป้องกันการโทรที่เกิดขึ้นพร้อมกันใด ๆ จากการเกิดขึ้นโดยอนุญาตให้มีการโทรที่ใช้งานเพียงครั้งเดียวในแต่ละครั้งที่การโทรพร้อมกันจะรอให้การโทรที่ใช้งานอยู่ให้เสร็จสิ้น
Takesarg <targ>
ป้องกันการโทรที่เกิดขึ้นพร้อมกันเมื่อแชร์อาร์กิวเมนต์เดียวกันโดยอนุญาตให้มีการโทรที่ใช้งานเพียงครั้งเดียวในเวลาต่ออาร์กิวเมนต์ซึ่งการโทรพร้อมกันจะรอการโทรที่ใช้งานอยู่ให้เสร็จสิ้น
Takesargreturnsvalue <targ, tvalue>
สำหรับอาร์กิวเมนต์ที่กำหนดจะมีการโทรเพียงครั้งเดียวและการโทรที่ตามมาจะดึงค่าจากแคช นอกจากนี้ยังป้องกันการโทรที่เกิดขึ้นพร้อมกันใด ๆ จากการเกิดขึ้นโดยอนุญาตให้มีการโทรที่ใช้งานเพียงครั้งเดียวในเวลาต่ออาร์กิวเมนต์ซึ่งการโทรพร้อมกันจะรอการโทรที่ใช้งานอยู่ให้เสร็จสิ้น
Takesenumerationarg <targ>
การโทรพร้อมกันจะดำเนินการเพียงครั้งเดียวสำหรับอาร์กิวเมนต์ที่กำหนดในคอลเลกชันอาร์กิวเมนต์
คอลเลกชันอาร์กิวเมนต์ถูกปลดออกจากค่าที่การดำเนินการอยู่ในเที่ยวบินแล้ว
ดังนั้นการเรียกด้วย ["A", "B", "C"] และ ["B", "C", "D"] พร้อมกันจะส่งผลให้มีการโทรหนึ่งครั้งด้วย ["A", "B", "C" และอีกครั้งที่มี ["D"]
Takesenumerationargreturnsvalue <Targ, tvalue>
การโทรครั้งแรกและการโทรที่เกิดขึ้นพร้อมกันจะดำเนินการเพียงครั้งเดียวสำหรับอาร์กิวเมนต์ที่กำหนดในคอลเลกชันอาร์กิวเมนต์ การโทรที่ตามมาจะดึงค่าจากแคช
คอลเลกชันอาร์กิวเมนต์ถูกถอดออกจากค่าที่มีค่าแคชอยู่หรือการดำเนินการอยู่ในเที่ยวบินแล้ว
ดังนั้นการเรียกด้วย ["A", "B", "C"] และ ["B", "C", "D"] พร้อมกันจะส่งผลให้มีการโทรหนึ่งครั้งด้วย ["A", "B", "C" และอีกครั้งที่มี ["D"] ครั้งต่อไป "A", "B", "C" หรือ "D" ถูกเรียกด้วยมันจะถูกถอดออกจากคอลเลกชันและค่าสำหรับมันจะถูกดึงออกมาจากแคช
วัตถุ ServiceConcurrency ทั้งหมดมีตัวสร้างแบบไม่มีพารามิเตอร์ซึ่งในกรณีนี้จะสร้าง ImemoryCache ภายใน
วัตถุ ServiceConcurrency ทั้งหมดที่ส่งคืนค่ายังมีตัวสร้างที่ยอมรับวัตถุ ImemoryCache ในกรณีที่คุณต้องการแชร์แคชกับวัตถุ ServiceConCurrency ทั้งหมด ถ้าคุณทำโปรดตรวจสอบให้แน่ใจว่าใช้คีย์ที่ไม่ซ้ำกันในรายการ
T Execute ( .. . )ดำเนินการคำขอเฉพาะ คุณให้การโทรกลับสำหรับการโทรภายนอก ดูตัวอย่างสำหรับข้อมูลเพิ่มเติมเนื่องจากข้อโต้แย้งและประเภทการส่งคืนเป็นประเภท serviceConcurrency เฉพาะ ค่าผลลัพธ์จะถูกแคชและมอบให้คุณในการโทรกลับของคุณ
void Reset ( )รีเซ็ตสถานะภายในเช่นสถานะสำหรับการโทรในกระบวนการและแคชภายใน (ในกรณีที่วัตถุ 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 ( )enumerators สำหรับแคชภายใน อนุญาตให้คุณใช้วัตถุในคำสั่ง foreach และ LINQ
void Dispose ( ) การกำจัดแคชภายในในกรณีที่ bool IsCacheShared เป็นเท็จ หากเป็นแคชที่ใช้ร่วมกันมันจะเรียก ResetCache แทน
TValue Value ;เฉพาะใน returnsvalue <value> นี่คือวัตถุแคชเดี่ยว
คุณสมบัติต่อไปนี้มีเฉพาะในวัตถุ ServiceConcurrency ที่ส่งคืนค่า
MemoryCacheEntryOptions CacheEntryOptions ;ตัวเลือกเหล่านี้จะถูกใช้ภายในเมื่อแคชมูลค่า การแก้ไขเหล่านี้ช่วยให้คุณสามารถตั้งค่าหมดอายุขนาดแคช ฯลฯ ดู MemoryCacheOptions สำหรับรายละเอียดเพิ่มเติม
bool IsCacheShared ;Getter เท่านั้น หมายถึงถ้ามีการแชร์แคชหรือไม่
ดำเนินการ () ยอมรับตัวแปลงค่าเสริมซึ่งสามารถแก้ไขค่าที่ดึงมาก่อนที่จะส่งคืนและแคช สิ่งนี้มีให้เฉพาะในวัตถุ 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 ( ) ;
}
}