Perpustakaan konkurensi dan negara bagian untuk .NET, bersinar dalam panggilan jaringan.
Paket Nuget tersedia di https://www.nuget.org/packages/serviceConcurrency/
Perpustakaan untuk menangani konkurensi dan negara bagian, terutama untuk kode yang memanggil layanan web.
Pertama, itu mencegah panggilan yang tidak perlu terjadi. Ketika panggilan dengan argumen yang cocok sudah dalam penerbangan, penelepon bersamaan diparkir dan akan dilanjutkan ketika permintaan yang berasal selesai. Jika argumennya adalah koleksi, entitas yang sudah dalam penerbangan dilucuti.
Kedua, itu menyimpan keadaan permintaan pengembalian nilai. Ketika nilai yang di -cache ada untuk permintaan yang diberikan, itu akan dikembalikan alih -alih panggilan yang dilakukan. Nilai ini dapat diakses kapan saja, mencegah kebutuhan untuk bidang dukungan tambahan dalam kode Anda. Jika argumennya adalah koleksi, entitas yang di -cache dilucuti dari argumen.
Perpustakaan ini menggunakan ImemoryCache untuk caching data internal.
Berikut beberapa hal yang dapat ditangani oleh serviceConcurrency untuk Anda. Contoh yang lebih mendalam dapat ditemukan di bagian berikutnya dari dokumen ini.
GetsessionToken () akan mengembalikan nilai cache setelah panggilan pertama dilakukan. Jika getsessionToken () disebut bersamaan sementara itu belum memiliki nilai di -cache, hanya satu panggilan untuk fetchsessionToken () yang akan pernah dilakukan dan panggilan bersamaan lainnya akan menghasilkan sampai nilai tersedia.
private ServiceConcurrency . ReturnsValue < string > sessionTokenState =
new ServiceConcurrency . ReturnsValue < string > ( ) ;
public async Task < string > GetSessionToken ( )
{
return await this . sessionTokenState . Execute ( this . FetchSessionToken ) ;
}Sama seperti di atas, tetapi pengumpulan argumen dilucuti nilai -nilai yang sudah di -cache atau dalam penerbangan. Jadi fetchUserProfiles () tidak akan pernah dipanggil dengan ID lebih dari sekali.
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
) ;
} Ada empat kelas di perpustakaan ini:
NoARGNOVALUE
Mencegah panggilan bersamaan dengan hanya memberikan satu panggilan aktif pada satu waktu, di mana panggilan bersamaan akan menunggu panggilan aktif selesai.
ReturnsValue <value>
Hanya satu panggilan yang akan dilakukan dan panggilan selanjutnya akan mengambil nilai dari cache. Juga mencegah panggilan bersamaan dari terjadi, dengan hanya mengizinkan satu panggilan aktif pada satu waktu, di mana panggilan bersamaan akan menunggu panggilan aktif selesai.
TakeArg <TARG>
Mencegah panggilan bersamaan ketika berbagi argumen yang sama, dengan mengizinkan hanya satu panggilan aktif pada waktu per argumen, di mana panggilan bersamaan akan menunggu panggilan aktif selesai.
Takerargreturnsvalue <Targ, TValue>
Untuk argumen yang diberikan, hanya satu panggilan yang akan dilakukan dan panggilan selanjutnya akan mengambil nilai dari cache. Juga mencegah panggilan bersamaan dari terjadi, dengan hanya mengizinkan satu panggilan aktif pada waktu per argumen, di mana panggilan bersamaan akan menunggu panggilan aktif selesai.
TakeenumerationArg <TARG>
Panggilan bersamaan hanya akan dieksekusi sekali untuk argumen yang diberikan dalam koleksi argumen.
Koleksi argumen dilucuti nilai -nilai yang sudah dioperasikan oleh suatu operasi.
Jadi secara bersamaan memanggil dengan ["A", "B", "C"] dan ["B", "C", "D"] akan menghasilkan satu panggilan dengan ["A", "B", "C"] dan satu dengan ["D"].
TakeenumerationRgreturnsValue <targ, tvalue>
Panggilan pertama, dan panggilan bersamaan, hanya akan dieksekusi sekali untuk argumen yang diberikan dalam koleksi argumen. Panggilan selanjutnya akan mengambil nilai dari cache.
Koleksi argumen dilucuti dari nilai -nilai yang ada nilai yang di -cache atau operasi sudah dalam penerbangan.
Jadi secara bersamaan memanggil dengan ["A", "B", "C"] dan ["B", "C", "D"] akan menghasilkan satu panggilan dengan ["A", "B", "C"] dan satu dengan ["D"]. Lain kali "A", "B", "C" atau "D" disebut, itu akan dilucuti dari koleksi dan nilai untuk itu akan diambil dari cache.
Semua objek serviceConcurrency memiliki konstruktor tanpa parameter, dalam hal ini iMemoryCache internal akan dibuat.
Semua objek serviceConcurrency yang mengembalikan nilai juga memiliki konstruktor yang menerima objek ImemoryCache, jika Anda ingin berbagi cache dengan semua objek serviceConcurrency. Jika Anda melakukannya, pastikan untuk menggunakan kunci unik pada entri.
T Execute ( .. . )Mengeksekusi permintaan tertentu. Anda memberikan panggilan balik untuk melakukan panggilan luar. Lihat contoh untuk informasi lebih lanjut, karena argumen dan jenis pengembalian adalah tipe serviceConcurrency spesifik. Nilai yang dihasilkan di -cache dan diberikan kepada Anda dalam panggilan balik Anda.
void Reset ( )Menentukan kembali keadaan internal, IE state untuk panggilan dalam proses dan cache internal (jika objek serviceConcurrency mengembalikan nilai).
bool IsExecuting ( )Mengembalikan apakah objek serviceConcurrency saat ini melaksanakan permintaan tertentu.
Metode berikut hanya tersedia di Objek ServiceConcurrency yang mengembalikan nilai.
void ResetCache ( )Mengatur ulang cache internal. Juga dipanggil dari reset ().
bool TryGetValue ( TArg key , out TValue value )Mendapat nilai dari cache.
void Set ( TArg key , TValue value )Setter cache dalam kasus langka Anda mungkin perlu memanipulasi cache secara manual.
void Remove ( TArg key )Menghapus nilai dari cache.
bool ContainsKey ( TArg key )Periksa apakah ada entri dalam cache.
TValue this [ TArg key ]Operator array untuk mengatur dan mendapatkan entri cache. Melempar keynotfoundException di pengambil jika entri tidak ada.
IEnumerator < KeyValuePair < TArg , TValue > > GetEnumerator ( )
IEnumerator IEnumerable . GetEnumerator ( )Pencacah untuk cache internal. Memungkinkan Anda menggunakan objek dalam pernyataan foreach dan LINQ.
void Dispose ( ) Membuang cache internal jika bool IsCacheShared salah. Jika itu adalah cache bersama, ia hanya akan memanggil ResetCache sebagai gantinya.
TValue Value ;Hanya dalam returnsvalue <value>. Ini adalah objek yang di -cache tunggal.
Properti berikut hanya tersedia di Objek ServiceConcurrency yang mengembalikan nilai.
MemoryCacheEntryOptions CacheEntryOptions ;Opsi -opsi ini digunakan secara internal saat nilai di -cache. Mengedit ini memungkinkan Anda untuk mengatur kedaluwarsa, ukuran cache dll. Lihat MemoryCacheOptions untuk detail lebih lanjut.
bool IsCacheShared ;Hanya mendapatkan. Menunjukkan apakah cache dibagikan atau tidak.
EXECUTE () menerima konverter nilai opsional, yang dapat memodifikasi nilai yang diambil sebelum mengembalikan dan caching. Ini hanya tersedia di objek serviceConcurrency yang mengembalikan nilai.
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 } " ;
) ;
}Objek ServiceConcurrency juga menerima parameter tambahan yang menyatakan jenis nilai dari permintaan internal. Ketika ini ditentukan, konverter nilai juga akan dikonversi antara jenis sumber dan tipe tujuan. Ini berguna jika permintaan yang diminta oleh Execute () adalah jenis yang berbeda dari bidang pendukung yang diinginkan.
// 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 ( ) ;
}
}