spring-data-cassandra-reactive improve performance of ReactiveCrudRepository.save - cassandra

I have simple showcase with ReactiveCrudRepository, which shows almost 2x difference in insertion speed using ReactiveCrudRepository.save() vs ReactiveCqlOperations.execute()
Here is the code:
Entity:
#Table("snapshot")
#Value
#Builder
#EqualsAndHashCode(callSuper = false)
#RequiredArgsConstructor
public class SnapshotRecord {
#PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
long id;
#PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED)
short market;
#PrimaryKeyColumn(ordinal = 3, type = PrimaryKeyType.CLUSTERED)
Instant slot;
double value;
}
Repository:
public interface SnapshotRepository extends ReactiveCrudRepository<SnapshotRecord, Long> {
default Mono<Boolean> saveViaCql(ReactiveCqlOperations cqlOps, SnapshotRecord record) {
return cqlOps.execute(
"INSERT INTO snapshot (id, market,slot,value) VALUES (?,?,?,?) USING TIMESTAMP ?;",
ps -> {
return ps.bind(
record.getId(),
record.getMarket(),
record.getSlot(),
record.getValue(),
record.getSlot().toEpochMilli() * 1000
);
}
);
}
}
Runner:
Flux<SnapshotRecord> data = Flux.generate(Object::new, (state, sink) -> {
ThreadLocalRandom random = ThreadLocalRandom.current();
sink.next(
new SnapshotRecord(
random.nextLong(),
(short) random.nextInt(),
Clock.systemUTC().instant(),
random.nextDouble()
)
);
return state;
});
subscription = data
//.flatMap((SnapshotRecord record) -> repository.saveViaCql(cqlOps, record), 512, 2048)
.flatMap(repository::save, 512, 2048) //doing this runs almost 2x slower than previous line
.doOnNext(d -> success.incrementAndGet())
.onErrorContinue((throwable, object) -> fail.incrementAndGet())
.subscribe();
Full project link on Github:
https://github.com/piddubnyi/spring-data-cassandra-performnace
Is there any way to improve insertion performance of repository.save(), or spring-proxy is really costly in this case?

Related

What's the right way to actually get this search functionality to work?

I have this app which displays a list of "coins" to the users . This list was parsed from an JSON API and I used Jetpack Compose for the UI. I implemented
Here is the code of the Jetpack composable list of "coins"
#Composable
fun CoinListScreen(
navController: NavController,
viewModel: CoinListViewModel = hiltViewModel(),
) {
val state = viewModel.state.value
Surface {
Box(modifier = Modifier.fillMaxSize()) {
Column {
androidx.compose.foundation.Image(painter = painterResource(id = R.drawable.ic_baseline_currency_bitcoin_24),
contentDescription = "BTC",
modifier = Modifier
.fillMaxWidth()
.align(CenterHorizontally)
.size(50.dp, 50.dp)
)
SearchBar(
hint = "Search..",
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
){
viewModel.searchCoinsList(it) **//here I'm calling my search function from the view model, inside my search bar**
}
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(state.coins) { coin ->
Spacer(modifier = Modifier.height(5.dp))
CoinListItem(
coin = coin,
onItemClick = {
navController.navigate(Screen.CoinDetailScreen.route + "/${coin.id}")
}
)
Divider()
}
}
}
if (state.error.isNotBlank()) {
Text(
text = state.error,
color = MaterialTheme.colors.error,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp)
.align(Alignment.Center)
)
}
if (state.isLoading) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
}
}
}
**//and this Is my composable search bar**
#Composable
fun SearchBar(
modifier: Modifier = Modifier,
hint: String = "",
onSearch: (String) -> Unit = {}
) {
var text by remember {
mutableStateOf("")
}
var isHint by remember {
mutableStateOf(hint != "")
}
Box(modifier = modifier){
BasicTextField(
value = text,
onValueChange = {
text = it
onSearch(it)
},
maxLines = 1,
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.shadow(5.dp, CircleShape)
.background(Color.White, CircleShape)
.padding(horizontal = 20.dp, vertical = 12.dp)
.onFocusChanged {
isHint = it.isFocused != true
}
)
if(isHint){
Text(
text = hint,
color = Color.LightGray,
modifier = Modifier.padding(horizontal = 20.dp, vertical = 12.dp)
)
}
}
}
and this is my view model, this is where I'm implementing the search function, this is where I'm lost, variables that I'm searching for are name, rank, and symbol from the Coin domain list
#HiltViewModel //injecting the use case
class CoinListViewModel #Inject constructor (
private val getCoinsUseCase: GetCoinsUseCase,
) : ViewModel() {
//vmstate Live Template, only the view model touches it
private val _state =
mutableStateOf(CoinListState())
val state: State<CoinListState> = _state
**//for search purposes , this is where I'm lost**
private var coinsList = mutableStateOf<List<Coin>>(listOf())
private var cachedCoinsList = listOf<Coin>()
private var isSearchStarting = true
private var isSearching = mutableStateOf(false)
init {
getCoins()
}
**//for search purposes , this is where I'm lost**
fun searchCoinsList(query: String){
val listToSearch = if(isSearchStarting){
coinsList.value
} else {
cachedCoinsList
}
viewModelScope.launch(Dispatchers.Default) {
if(query.isEmpty()){
coinsList.value = cachedCoinsList
isSearching.value = false
isSearchStarting = true
return#launch
}
val results = listToSearch.filter {
//val iterate: Int = coins.size
it.name.contains(query.trim(), ignoreCase = true) ||
(it.rank.toString() == query.trim()) ||
it.symbol.contains(query.trim(), ignoreCase = true)
}
if(isSearchStarting){
cachedCoinsList = coinsList.value
isSearchStarting = false
}
coinsList.value = results
isSearching.value = true
}
}
//function that calls our GetCoinsUseCase and puts the data inside the state object
//to display that in the UI
private fun getCoins() {
//overwrote the invoke function earlier for the use case which allows us to call the use case as a function
getCoinsUseCase().onEach { result ->
when (result) {
is Resource.SUCCESS -> {
_state.value =
CoinListState(coins = result.data ?: arrayListOf())
}
is Resource.ERROR -> {
_state.value =
CoinListState(
error = result.message ?: "An unexpected error occurred"
)
}
is Resource.LOADING -> {
_state.value = CoinListState(isLoading = true)
}
}
}.launchIn(viewModelScope)
}
}
CoinsListState data class used in view model
data class CoinListState(
val isLoading: Boolean = false,
val coins: ArrayList<Coin> = arrayListOf(),
val error: String = ""
)
this is my "GetCoinsUseCase" to get the coins
class GetCoinsUseCase #Inject constructor(
private val repository: CoinRepository
) {
// overwriting the operator fun invoke allows us to call the use case
//GetCoinsUseCase as if it was a function, and we return a flow because
// we want to emit states LOADING -> for progress bar, SUCCESS -> attach list of coins,
// and ERROR
operator fun invoke(): kotlinx.coroutines.flow.Flow<Resource<ArrayList<Coin>>> = flow {
try {
emit(Resource.LOADING<ArrayList<Coin>>())
//we mapped it to toCoin because we returning a list of coin, not coinDTO
val coins = repository.getCoins().map { it.toCoin() }
emit(Resource.SUCCESS<ArrayList<Coin>>(coins as ArrayList<Coin>))
}catch (e: HttpException){
emit(Resource.ERROR<ArrayList<Coin>>(e.localizedMessage ?: "An unexpected error occurred"))
}catch (e: IOException){
emit(Resource.ERROR<ArrayList<Coin>>("Couldn't reach server. Check connection"))
}
}
}
just the coin repository that is implemented in another place
interface CoinRepository {
//repository definitions
suspend fun getCoins() : ArrayList<CoinDTO>
suspend fun getCoinById(coinId: String) : CoinDetailDTO
}
This is my domain - Domain - only contains the data needed
data class Coin(
var id: String,
var isActive: Boolean,
var name: String,
var rank: Int,
var symbol: String
)
and this is how I'm mapping it
data class CoinDTO(
val id: String,
#SerializedName("is_active")
val isActive: Boolean,
#SerializedName("is_new")
val isNew: Boolean,
val name: String,
val rank: Int,
val symbol: String,
val type: String
)
fun CoinDTO.toCoin(): Coin {
return Coin(
id = id,
isActive = isActive,
name = name,
rank = rank,
symbol = symbol,
// logo = CoinDetailLogo(logo = String()).logo
)
}
Coin list item if needed for reference, this is what is displayed to the user in the list
#Composable
fun CoinListItem (
coin: Coin,
onItemClick: (Coin) -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onItemClick(coin) }
.padding(20.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = "${coin.rank}. ${coin.name} (${coin.symbol})",
style = MaterialTheme.typography.body1,
overflow = TextOverflow.Ellipsis
)
Text(
text = if(coin.isActive) "active" else "inactive",
color = if(coin.isActive) Color.Green else Color.Red,
fontStyle = FontStyle.Italic,
textAlign = TextAlign.End,
style = MaterialTheme.typography.body2,
modifier = Modifier.align(CenterVertically)
)
}
}
as well as the "Resource" generic for states
//UIStates
sealed class Resource<T>(val data: T? = null, val message: String? = null) {
class SUCCESS<T>(data: T) : Resource<T>(data)
class ERROR<T>(message: String, data: T? = null) : Resource<T>(data, message)
class LOADING<T>(data: T? = null) : Resource<T>(data)
}
again, given this info, how can I get the function searchCoinList in the view model to correctly view the searched data (name, rank, or symbol) when it is called in the CoinListScreen inside the Search Bar. Thank you so much
It seems like you want to implement a basic instant search functionality. It's pretty easy to achieve using Kotlin's StateFlow and its operators. Consider the following implementation with description:
// CoinListViewModel
private val queryFlow = MutableStateFlow("")
private val coinsList = mutableStateOf<List<Coin>>(listOf())
init {
queryFlow
.debounce(300) // filters out values that are followed by the newer values within the given timeout. The latest value is always emitted.
.filterNot { query -> userInput.isEmpty() } // filter the unwanted string like an empty string in this case to avoid the unnecessary network call.
.distinctUntilChanged() // to avoid duplicate network calls
.flowOn(Dispatchers.IO) // Changes the context where this flow is executed to Dispatchers.IO
.flatMapLatest { query -> // to avoid the network call results which are not needed more for displaying to the user
getCoinsUseCase(query).catch { emitAll(flowOf(emptyList())}
}
.onEach { coins: List<Coin> -> // go through each list of Coins
coinsList.value = coins
}
.launchIn(viewModelScope)
}
fun searchCoinsList(query: String) {
queryFlow.value = query
}

What changes are required to make this class scan Barcode in any direction. It works only for QR codes

I am developing a Qr Code and Barcode Scanner App, using CameraX and Zxing but the following class only works for Qr Code. I want to scan barcodes as well from any orientation on an android device
Code Scanning class:
public class QRCodeImageAnalyzer implements ImageAnalysis.Analyzer {
private QRCodeFoundListener listener;
public QRCodeImageAnalyzer(QRCodeFoundListener listener) {
this.listener = listener;
}
#SuppressLint("UnsafeOptInUsageError")
#Override
public void analyze(#NonNull ImageProxy imageProxy) {
Image image = imageProxy.getImage();
if (image.getFormat() == YUV_420_888 || image.getFormat() == YUV_422_888 || image.getFormat() == YUV_444_888) {
ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
byte[] imageData = new byte[byteBuffer.capacity()];
byteBuffer.get(imageData);
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(
imageData,
image.getWidth(), image.getHeight(),
0, 0,
image.getWidth(), image.getHeight(),
false
);
BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(source));
try {
Result result = new QRCodeMultiReader().decode(binaryBitmap);
listener.onQRCodeFound(result.getText());
} catch (FormatException | ChecksumException | NotFoundException e) {
listener.qrCodeNotFound();
}
}
imageProxy.close();
}
}
I also tried this class CameraX with MLKit: I tried the sample from the official docs of MLKit provided in the first answer but it scan nothing neither QR Code nor Barcode. Please take a look if I put things the wrong way.
import android.annotation.SuppressLint
import android.util.Log
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import com.google.mlkit.vision.barcode.Barcode
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.common.InputImage
#UseExperimental(markerClass = [ExperimentalGetImage::class])
class QrCodeAnalyzer(
private val onQrCodesDetected: (qrCodes: List<Barcode>?) -> Unit
) : ImageAnalysis.Analyzer {
private val TAG: String = "Kamran"
private fun rotationDegreesToFirebaseRotation(rotationDegrees: Int): Int {
return when (rotationDegrees) {
0 -> 0
90 -> 1
180 -> 2
270 -> 3
else -> throw IllegalArgumentException("Not supported")
}
}
#SuppressLint("UnsafeOptInUsageError")
override fun analyze(image: ImageProxy) {
val rotation = rotationDegreesToFirebaseRotation(image.imageInfo.rotationDegrees)
image.image?.let{
val optionss = BarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE,
Barcode.FORMAT_EAN_8,
Barcode.FORMAT_EAN_13)
.build()
val imageValue = InputImage.fromMediaImage(it, image.imageInfo.rotationDegrees)
//val options = BarcodeScannerOptions.Builder().setBarcodeFormats(Barcode.FORMAT_QR_CODE).build()
val scanner = BarcodeScanning.getClient(optionss)
scanner.process(imageValue)
.addOnCompleteListener { barcodes ->
barcodes.result?.forEach { barcode ->
val bounds = barcode.boundingBox
val corners = barcode.cornerPoints
val rawValue = barcode.rawValue
}
onQrCodesDetected(barcodes.result)
image.image?.close()
image.close()
Log.d(TAG, "Successfully got inside onCompleteListener")
}
.addOnFailureListener { failure ->
failure.printStackTrace()
image.image?.close()
image.close()
}
}
}
}
I am not sure If using ZXing android library is a requirement (afaik it is no longer developed). If it is not a hard requirement, I would suggest you to give it a try for MLKit Barcode Scanner. It scans QR as well.
Also, you would probably find it easier to implement with CameraX as they already have multiple examples and sample code.
For example check this guide for step by step instructions.
Particularly this part.
You can configire the scanner first:
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE,
Barcode.FORMAT_EAN_8,
Barcode.FORMAT_EAN_13)
.build()
then;
private class YourImageAnalyzer : ImageAnalysis.Analyzer {
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
// Pass image to an ML Kit Vision API
// ...
}
}
}
finally this is how you get it decoded:
val result = scanner.process(image)
.addOnSuccessListener { barcodes ->
// Task completed successfully
// ...
}
.addOnFailureListener {
// Task failed with an exception
// ...
}

Is it possible to nest JUnit 5 parameterized tests?

I am attempting to write a parameterized test for an interface Foo, which declares a method getFooEventInt(int, int). I have written a paramterized test that works for a single instance of Foo (a FooImpl object).
public class FooTest {
#ParameterizedTest
#MethodSource("getFooEvenIntProvider")
public void getFooEvenIntTest(int seed, int expectedResult) {
Foo foo = new FooImpl();
Assertions.assertEquals(expectedResult, foo.getFooEvenInt(seed));
}
private static Stream getFooEvenIntProvider() {
return Stream.of(
Arguments.of(-2, 0),
Arguments.of(-1, 0),
Arguments.of( 0, 2),
Arguments.of( 1, 2),
);
}
}
However, I'd like to be able to have getFooEvenIntTest(int, int) be invoked against a provided list of Foo implementation instances, with each iteration then using the provide list of seed/expectedResult values.
I realize I could do this as...
public class FooTest {
#ParameterizedTest
#MethodSource("getFooProvider")
public void getFooImplEvenIntTest(Foo foo) {
int[] expectedResult = { 0, 0, 2, 2 };
int[] seed = { -2, -1, 0, 1 };
for(int i=0; i<seed.length; i++) {
Assertions.assertEquals(expectedResult[i],
foo.getFooEvenInt(seed[i]));
}
}
private static Stream getFooProvider() {
return Stream.of(
Arguments.of(new FooImpl()),
Arguments.of(new FooImpl2())
);
}
}
Any ideas? I'll post if I figure it out, but I thought I'd check to see if this is even doable, or if there's a different approach.
I guess you think of combining two arguments streams. You could achieve this by creating the cartesian product of two arguments lists.
I have implemented that on https://github.com/joerg-pfruender/junit-goodies/blob/master/src/main/java/com/github/joergpfruender/junitgoodies/ParameterizedTestHelper.java
public static Stream<Arguments> cartesian(Stream a, Stream b) {
List argumentsA = (List) a.collect(Collectors.toList());
List argumentsB = (List) b.collect(Collectors.toList());
List<Arguments> result = new ArrayList();
for (Object o : argumentsA) {
Object[] objects = asArray(o);
for (Object o1 : argumentsB) {
Object[] objects1 = asArray(o1);
Object[] arguments = ArrayUtils.addAll(objects, objects1);
result.add(Arguments.of(arguments));
}
}
return result.stream();
}
private static Object[] asArray(Object o) {
Object[] objects;
if (o instanceof Arguments) {
objects = ((Arguments) o).get();
} else {
objects = new Object[]{o};
}
return objects;
}
Then your test code will be:
public static Stream<Arguments> fooIntsAndFooProvider() {
return ParameterizedTestHelper.cartesian(getFooEvenIntProvider(), getFooProvider());
}
#ParameterizedTest
#MethodSource("fooIntsAndFooProvider")
public void getFooImplEvenIntTest(Integer seed, Integer expectedResult, Foo foo) {
Assertions.assertEquals(expectedResult,
foo.getFooEvenInt(seed));
}
BLUF: I will interpret the crickets to mean "even if you could, you shouldn't be nesting parameterized tests", in which case I run with the approach outlined below.
For an interface Foo...
public interface Foo {
public char getFirstChar(String strValue);
public int getNextEvenInt(int seed);
}
The "best" use of parameterized tests for implementations of Foo would be...
public class FooTest {
#ParameterizedTest
#MethodSource("getFooProvider")
public void getFirstCharTest(Foo foo) {
char[] expectedResult = { 'a', 'b', 'c', 'd' };
String[] seed = { "alpha", "bravo", "charlie", "delta" };
for(int i=0; i<seed.length; i++) {
Assertions.assertEquals(expectedResult[i],
foo.getFirstChar(seed[i]));
}
}
#ParameterizedTest
#MethodSource("getFooProvider")
public void getNextEvenIntTest(Foo foo) {
int[] expectedResult = { 0, 0, 2, 2 };
int[] seed = { -2, -1, 0, 1 };
for(int i=0; i<seed.length; i++) {
Assertions.assertEquals(expectedResult[i],
foo.getFooEvenInt(seed[i]));
}
}
private static Stream getFooProvider() {
return Stream.of(
Arguments.of(new FooImplOne()),
Arguments.of(new FooImplTwo())
// extend as need for implementations of Foo
);
}
}
While I won't get the "warm fuzzies" of seeing the passing results for each value-pair in the various tests, it will fulfill my goal of having a test at the interface level that I can easily extend to validate/verify the interface's implementations.

Java 8 CompletedFuture web crawler doesn't crawl past one URL

I'm playing with the newly introduced concurrency features in Java 8, working exercises from the book "Java SE 8 for the Really Impatient" by Cay S. Horstmann. I created the following web crawler using the new CompletedFuture and jsoup. The basic idea is given a URL, it'll find first m URLs on that page and repeat the process n times. m and n are parameters, of course. Problem is the program fetches the URLs for the initial page but doesn't recurse. What am I missing?
static class WebCrawler {
CompletableFuture<Void> crawl(final String startingUrl,
final int depth, final int breadth) {
if (depth <= 0) {
return completedFuture(startingUrl, depth);
}
final CompletableFuture<Void> allDoneFuture = allOf((CompletableFuture[]) of(
startingUrl)
.map(url -> supplyAsync(getContent(url)))
.map(docFuture -> docFuture.thenApply(getURLs(breadth)))
.map(urlsFuture -> urlsFuture.thenApply(doForEach(
depth, breadth)))
.toArray(size -> new CompletableFuture[size]));
allDoneFuture.join();
return allDoneFuture;
}
private CompletableFuture<Void> completedFuture(
final String startingUrl, final int depth) {
LOGGER.info("Link: {}, depth: {}.", startingUrl, depth);
CompletableFuture<Void> future = new CompletableFuture<>();
future.complete(null);
return future;
}
private Supplier<Document> getContent(final String url) {
return () -> {
try {
return connect(url).get();
} catch (IOException e) {
throw new UncheckedIOException(
" Something went wrong trying to fetch the contents of the URL: "
+ url, e);
}
};
}
private Function<Document, Set<String>> getURLs(final int limit) {
return doc -> {
LOGGER.info("Getting URLs for document: {}.", doc.baseUri());
return doc.select("a[href]").stream()
.map(link -> link.attr("abs:href")).limit(limit)
.peek(LOGGER::info).collect(toSet());
};
}
private Function<Set<String>, Stream<CompletableFuture<Void>>> doForEach(
final int depth, final int breadth) {
return urls -> urls.stream().map(
url -> crawl(url, depth - 1, breadth));
}
}
Test case:
#Test
public void testCrawl() {
new WebCrawler().crawl(
"http://en.wikipedia.org/wiki/Java_%28programming_language%29",
2, 10);
}
The problem is in the following code:
final CompletableFuture<Void> allDoneFuture = allOf(
(CompletableFuture[]) of(startingUrl)
.map(url -> supplyAsync(getContent(url)))
.map(docFuture -> docFuture.thenApply(getURLs(breadth)))
.map(urlsFuture -> urlsFuture.thenApply(doForEach(depth, breadth)))
.toArray(size -> new CompletableFuture[size]));
For some reason you are doing all this inside a stream of one element (is that a part of the exercise?). The result is that allDoneFuture is not tracking the completion of the sub-tasks. It's tracking the completion of the Stream<CompletableFuture> that comes from doForEach. But that stream is ready right away and the futures inside of it are never asked to complete.
Fix it by removing the stream that doesn't do anything helpful:
final CompletableFuture<Void> allDoneFuture=supplyAsync(getContent(startingUrl))
.thenApply(getURLs(breadth))
.thenApply(doForEach(depth,breadth))
.thenApply(futures -> futures.toArray(CompletableFuture[]::new))
.thenCompose(CompletableFuture::allOf);

How to break from List<T>.AsParallel().ForAll() using C#

I am using List.AsParallel().ForAll() PLINQ implementation. Inside the loop, if I find a condition is successful, I want the loop to break immediately out of the ForAll() loop. How can I achieve it?
Here is the sample code.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Basics
{
class Program
{
class Family
{
public int SNo { get; set; }
public string Name { get; set; }
}
static List<Family> families = null;
static Program()
{
families = new List<Family>()
{
new Family(){SNo = 10, Name="Richard"},
new Family(){SNo = 33, Name="Xio Fung"},
new Family(){SNo = 10, Name="Sean"},
new Family(){SNo = 10, Name="Andrea"},
new Family(){SNo = 20, Name="Bjorn"},
new Family(){SNo = 20, Name="Isabella"},
new Family(){SNo = 35, Name="Michael"},
new Family(){SNo = 35, Name="Marie"}
};
}
private static void Main()
{
Dictionary<int, List<Family>> theFamily = new Dictionary<int, List<Family>>();
var groupedFamilies = families.GroupBy(family => family.SNo);
groupedFamilies.AsParallel().ForAll(groupedFamily =>
{
int groupedFamilyCount = groupedFamily.Count();
if (groupedFamilyCount == 1)
{
Console.WriteLine(groupedFamily.FirstOrDefault().Name);
// break; <-- I want to break if I find the count = 1
}
});
Console.ReadLine();
}
}
}
Regards,
Sriram
I believe Parallel.ForEach will work:
Parallel.ForEach(groupedFamilies.AsParallel(), (groupedFamily, loopState) =>
{
int groupedFamilyCount = groupedFamily.Count();
if (groupedFamilyCount == 1)
{
Console.WriteLine(groupedFamily.FirstOrDefault().Name);
loopState.Stop(); // break if I find the count = 1
}
});
Depending on your use case, you might need loopState.Break instead. Use Stop() for Any-like operations, Break() for First-like ones.
There's a good article on the subject in Microsoft's articles on parallel programming with .NET 4 called "When To Use Parallel For Each Or PLINQ?"

Resources