LINQ 이중 슬립 현상

분명 제가 잘못한것이겠지만 그냥 답변을 바라기 보다는 비슷한 현상을 겪어보신분 있나 해서 올려봅니다.

이중슬립 : 전자의 운동이 관찰의 따라 다르게 발생하는 현상

코드는 예를 들어

c.list = rt.detaillst.Where(p => p.idx== c.idx  && p.SystemName.Trim() == "test").ToLiST()
평범하게 프로시져 받아온 데이타를 List 에 담았습니다.

그리고 이걸로  c.list.Count 를 갯수를 헤아렸습니다.

근데 0 이 나옵니다. 분명히 데이타가 있음에도 ;;;

진짜 원인도 모르겠고 원격지 서버라 디버깅도 안되고 하도 답답해서

	foreach (var r in rt.detaillst)
	{
	 _logger.LogInformation("00:" +r.idx.ToString() +"|" );
	}
부모 리스트 값을 LOG를 찍으니까 Count 값이 3으로 다시 나옵니다.

이게 말이 되는 상황인가 전체적인 코드나 환경을 공유할수 없지만

아니 관찰하지 않을때는 값이 없는데
Log로 관찰을 하니까 데이타 존재하는 현상

예전에는 이런 현상이 없는데 데이타가 커지면서 요근래 생겼습니다.
(10만개 정도)

예전부터 Linq 로 데이타 다룰떄 10만개가 넘으면 뭔가 이상작동을 했던 경험이 있어
Linq 에 대한 불신이 약간 있긴 한데 이번경우는 너무 황당하네요

당연히 제가 뭐 잘못해서 발생한 현상이고 이유는 있을것고 나중에
아 나 바보인가 하겠지만 지금은 그렇네요

2개의 좋아요

정확히 기억은 안나지만 저도 이런 이중슬릿 현상을 겪은적이 있었는데
제 경우에는 다중 쓰레드 상황에서 그런 일이 벌어졌던걸로 기억합니다.

슈뢰딩거의 LinQ
제가 이래서 웬만하면 직접 만들어 씁니다.
원인이 밝혀지면 별 거 아닐 수도 있지만 숙련된 개발자도 헷갈릴 수 있다면 안 쓰는 게 낫다고 봐요

1개의 좋아요

그런데, Where 조건이 foreach에도 있어야 공평하지 않을까요?

슬릿과 슬립이 다른 것처럼, Where 조건의 여부는 차이가 큽니다. ^^

4개의 좋아요

@kevin13 님이 말씀하신 것 처럼

원본 데이터의 유무랑 무관하게

작성하신 Where 절 조건에 부합하지 않아서

Count 값이 0 이 나오는거 아닐까요.

SomeType current = new(); // c
DataTable response = new(); // rt

int currentIndex = current.idx;
int sourceCount = response.detaillist.Count();
List<SomeType> testResults = response.detaillist
    .Where(row => row.idx == currentIndex)
    .Where(row => row.SystemName.Trim() == "test")
    .ToList();

int resultCount = testResults.Count();

_logger.LogInformation("Exected: {expected}, Actual: {actual}", sourceCount , resultCount );
4개의 좋아요

where절 조건에 충족하는 item이 없을지도 모르겠군요

1개의 좋아요

윗 분들의 의견도 중요한 것 같고 제 의견은 Trim()이 걸립니다. 공백이 Trim()으로 제거되지 않았을 때 의도한 결과가 안나올 것 같거든요.

이 때 공백의 유무는 char.IsWhiteSpace()로 판단합니다.

5개의 좋아요

네 저도 where condition 뭔가 다른값이 있는것 같습니다 trim 으로 제거 안되는
wt 라든가 오늘은 또 잘되는것 같네요

1개의 좋아요

별로 상관 없는 얘기일수도 있지만 카테고리가 F# Q&A로 되어 있네요.

1개의 좋아요

아 수정했습니다 ㅎ

1개의 좋아요

클릭 참기 힘든 제목…ㅎㅎ

2개의 좋아요

rt.detaillst가 어떻게 만들어졌는지 알아야 원인을 추측해 볼 수 있지 않을까요?
foreach돌려서 잘 나온다는건 foreach돌리는 순간 rt.detaillst가 평가되었다는거고, 타이밍 이슈가 해소되었다고 보여져요. 위에 @fizzy 님 말씀 처럼요.
그리고 c.idx도 중요해 보입니다.

rt.detailst dapper에서 받아온 procedure 결과값입니다.
테이블 두개를 반환하면 1만개 10만개 레코드가 있었습니다.

가져온 값을 첫번쨰 테이블 키로 해서 두번째 테이블에서 데이타를
찾는 로직을 code상에서 처리했습니다.

1만개 키값으로 두번쨰 테이블 search 합니다. 처음에는 paller 병렬 처리로
돌리다가 아무래도 뭐가 이상한것 같아서 그냥 일반 순차 처리 했습니다.

아 뭔가 소스를 공유할만한 성격은 아닌것 같고 ;;
그냥 제착각이라고 생각하고 싶습니다 .
답변 달아주신분들 그냥 가십거리라고 생각해주시면 감사하겠습니다.^^

1개의 좋아요

대퍼 문제 아닐까요???(지연로딩 관련이라던지…)
쿼리에 조건이 들어가던지 혹은 dataillst를 먼저 ToArray()등을 했으면 문제가 절대 안생겼을거같은 느낌이 드네여

다른 분들이 말씀하신 내용이지만 Where 조건에서 결과 차이가 있는 것 같습니다.
구체적으로는, Trim의 경우 문자열 끝에 null문자가 들어간 것은 아닐까… 의심도 되구요.
그래서 혹시 모르니 TrimEnd(‘\0’) 또는 Replace하셔서 null문자를 제거해보시는 것도 좋을 것 같습니다.

p.SystemName.TrimEnd('\0')
// 또는
p.SystemName.Replace("\0", "");

범인이 꼭 잡혔으면 좋겠네요…
그리고 LINQ에서 이런저런 이슈가 공공연하게 발생되는지는 댓글보고 처음 알았어요.
다들 신뢰를 못하시는 듯해서 솔직히 충격적입니다. :scream:
저는 그동안 당연히 프로그램 언어니까 잘 작동되겠거니… 했거든요.

그나저나 양자역학적 접근이 너무 재밌네요…ㅋㅋ
관련이 있을런지는 모르겠으나, 짤 하나 투척하고 가봅니다…

2개의 좋아요

지연된 평가 때문에 문제가 발생할 수 있습니다. 예를 들어, Where() 메서드를 사용해서 필터링된 결과 목록을 얻은 후 원본 데이터를 수정하면, 이미 생성된 결과 목록이 변하지 않아야 된다고 보통 생각하는데요. 그러나 다시 목록을 조회할 때 원본 데이터의 변경 사항이 반영되어 목록이 달라지는 경우가 발생할 수 있습니다.

다음은 예시 코드입니다.

int[] sourceItems = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

var oddItems = sourceItems.Where(x => x % 2 != 0);
// 1, 3, 5, 7, 9 출력
foreach (var item in oddItems)
{
    Console.WriteLine(item);
}

Console.WriteLine();

sourceItems[1] = 11;
// 1, 3, 5, 7, 9 출력 되어야 할 것 같지만 1, 11, 3, 5, 7, 9 출력
foreach (var item in oddItems)
{
    Console.WriteLine(item);
}

저도 Trim 이 의심스러워 찾아 봤습니다.

String.Trim Method (System) | Microsoft Learn

제일 마지막에 호출자 주의 사항이 있는데, string.Trip() 메서드는:

.Net 4 이상에서는 아래의 두 문자를 지우지 않는다고 합니다.

  • ZERO WIDTH SPACE (U+200B)
  • ZERO WIDTH NO-BREAK SPACE (U+FEFF)

이 이하 버전에서는 아래의 세 문자를 지우지 않는다고 하네요.

  • MONGOLIAN VOWEL SEPARATOR (U+180E)
  • NARROW NO-BREAK SPACE (U+202F)
  • MEDIUM MATHEMATICAL SPACE (U+205F).

DB 가 보유한 문자열 데이터가 내 코드가 생산한 것이 아니라면, 특히 닷넷 시스템이 생성한 것이 아니라면, 닷넷의 문자열 처리 메서드를 그냥 쓰는 것은 좀 위험할 수도 있겠다는 생각이 드네요.

2개의 좋아요

개인적인 경험으로는 로우레벨 장비(영수증프린터 등)에서 제공하는 string 값이

C# 의 기본인 UTF-16 으로 변경되면서 표시는 안되는데 문자열에 포함이 되어있는 경우가 종종 있었습니다.

string value = "..withUnPrintableUnicode...";
string printable = Regex.Replace(value , @"\p{C}+", string.Empty);

위와 같이 정규식을 이용해 Non-Printable 문자를 제거하는 방법도 있구요.

추가적으로 대소문자가 중요한 부분이 아니라면

string.Equal(...) 메서드를 StringComparison.OrdinalIgnoreCase 조건으로 실행하는 것도 좋을 것 같습니다.

string A = "A";
string a = "a";

bool IsMatch = A.Equals(a, StringComparison.OrdinalIgnoreCase);
2개의 좋아요

이대로 덮으시면 안돼요!!
원인이 뭐였는지 다들 기다린다구요!
저도 벌써 현기증이 나고 있어요!

2개의 좋아요