좋아요, 정확히 IMapper
에 무엇이 있는지 확실하지 않지만 몇 가지를 제안하고, 그 중 일부는 다른 제한 사항으로 인해 실현 불가능할 수도 있습니다. 나는 이것을 생각해 보았을 때 거의 이것을 썼다. 나는 다음에 똑같은 일을 더 쉽게 할 수 있도록 행동의 생각의 기차를 보는 것이 도움이된다고 생각한다. (물론 내 솔루션을 좋아한다고 가정하십시오.
LINQ는 본질적으로 스타일로 작동합니다. 이는 이상적으로 검색어에 부작용이 없어야 함을 의미합니다. 예를 들어, 나는의 서명하는 방법을 기대할 :
public IEnumerable<User> MapFrom(IEnumerable<User> users)
오히려 기존 사용자를 돌연변이보다 추가 정보와 함께 사용자 개체의 새로운 시퀀스를 반환 할 수 있습니다. 현재 추가하고 유일한 정보는 아바타, 그래서의 라인을 따라 User
에 메소드를 추가 것 : 심지어 User
완전히 변경할 수 있도록 할 수 있습니다
public User WithAvatar(Image avatar)
{
// Whatever you need to create a clone of this user
User clone = new User(this.Name, this.Age, etc);
clone.FacebookAvatar = avatar;
return clone;
}
- 그 주변에 다양한 전략이있다, 빌더 패턴과 같은 자세한 내용을 원하면 저에게 물어보십시오. 어쨌든 중요한 것은 이전 사용자의 복사본 인 새 사용자를 만들었지 만 지정된 아바타가 있어야한다는 것입니다.
이
첫 번째 시도 : 내부는 당신의 매퍼에 다시 지금
가입 ... 당신은 현재 세 공공 방법을 가지고 있지만 는는 첫 번째는 공개 할 필요가 있다고 추측 내, 및 나머지 API는 실제로 Facebook 사용자를 노출 할 필요가 없습니다.GetFacebookUsers
메서드는 기본적으로 괜찮은 것처럼 보입니다.하지만 공백을 고려하여 쿼리를 정렬 할 수는 있습니다.
로컬 사용자의 시퀀스와 Facebook 사용자 모음을 고려하면 실제 매핑 비트가 남습니다. 직선의 "join"절은 일치하는 Facebook 사용자가없는 로컬 사용자를 양보하지 않기 때문에 문제가됩니다. 대신, 우리는 Facebook 사용자가 아바타가없는 것처럼 비 Facebook 사용자를 치료할 방법이 필요합니다. 본질적으로 이것은 null 객체 패턴입니다.
우리는 널 UID를 가지고 페이스 북 사용자와 오는에 의해 그렇게 할 수 있습니다 (개체 모델을 가정은 허용) : 그러나
// Adjust for however the user should actually be constructed.
private static readonly FacebookUser NullFacebookUser = new FacebookUser(null);
, 우리가 실제로 있기 때문에하는 순서 이러한 사용자의을 원한다 그 Enumerable.Concat
사용하는 작업은 다음과 같습니다
private static readonly IEnumerable<FacebookUser> NullFacebookUsers =
Enumerable.Repeat(new FacebookUser(null), 1);
이제 우리는 단순히 우리의 진짜이 더미 항목을 "추가", 그리고 정상적인 내부 조인 할 수 있습니다. 이
은으로 가정하고 Facebook 사용자의 조회는 항상 "실제"Facebook UID에 대한 사용자를 찾습니다. 그렇지 않은 경우에는 내부 조인을 사용하지 말고 다시 방문해야합니다.
우리는 다음 수행 끝에 "NULL"사용자를 포함 가입 및 프로젝트가 WithAvatar
를 사용하여 :
public IEnumerable<User> MapFrom(IEnumerable<User> users)
{
var facebookUsers = GetFacebookUsers(users).Concat(NullFacebookUsers);
return from user in users
join facebookUser in facebookUsers on
user.FacebookUid equals facebookUser.uid
select user.WithAvatar(facebookUser.Avatar);
}
을 따라서 전체 클래스가 될 것이다 : 여기
public sealed class FacebookMapper : IMapper
{
private static readonly IEnumerable<FacebookUser> NullFacebookUsers =
Enumerable.Repeat(new FacebookUser(null), 1);
public IEnumerable<User> MapFrom(IEnumerable<User> users)
{
var facebookUsers = GetFacebookUsers(users).Concat(NullFacebookUsers);
return from user in users
join facebookUser in facebookUsers on
user.FacebookUid equals facebookUser.uid
select user.WithAvatar(facebookUser.pic_square);
}
private Facebook.user[] GetFacebookUsers(IEnumerable<User> users)
{
var uids = (from u in users
where u.FacebookUid != null
select u.FacebookUid.Value).ToList();
// return facebook users for uids using WCF
}
}
몇 가지 포인트 :
- 앞에서 언급했듯이 사용자의 Facebook UID가 유효한 것으로 페치되지 않을 경우 내부 결합이 문제가됩니다. 사용자.
- 마찬가지로 Facebook 사용자가 중복되면 문제가 발생합니다. 각 로컬 사용자는 두 번 나오게됩니다.
- Facebook 사용자가 아닌 사용자의 아바타를 대체 (제거)합니다.
두 번째 방법 : 그룹은 우리가 이러한 점을 해결 할 수 있는지 보자
가입 할 수 있습니다. 우리가 페이 스북 사용자 중 단일 페이스 북의 UID를 가져온 경우, 그들 중 어떤 사람이 우리가 아바타를 차지하는지 상관하지 않는다고 가정합니다. 그들은 동일해야합니다.
우리가 필요로하는 것은 그룹 가입입니다. 따라서 각 로컬 사용자에게 일치하는 Facebook 사용자의 시퀀스가 제공됩니다. 그런 다음 편리하게 사용하기 위해 DefaultIfEmpty
을 사용합니다.
우리는 WithAvatar
을 이전과 동일하게 유지할 수 있습니다.하지만 이번에는 Facebook 사용자가 아바타를 가져 오는 경우에만 호출 할 것입니다. C# 쿼리 식의 그룹 조인은 join ... into
으로 표시됩니다. 이 쿼리는 상당히 길지만, 너무 무섭지는 않습니다. 정직합니다!
public IEnumerable<User> MapFrom(IEnumerable<User> users)
{
var facebookUsers = GetFacebookUsers(users);
return from user in users
join facebookUser in facebookUsers on
user.FacebookUid equals facebookUser.uid
into matchingUsers
let firstMatch = matchingUsers.DefaultIfEmpty().First()
select firstMatch == null ? user : user.WithAvatar(firstMatch.pic_square);
}
여기지만, 의견이 다시 쿼리 식입니다 :
// "Source" sequence is just our local users
from user in users
// Perform a group join - the "matchingUsers" range variable will
// now be a sequence of FacebookUsers with the right UID. This could be empty.
join facebookUser in facebookUsers on
user.FacebookUid equals facebookUser.uid
into matchingUsers
// Convert an empty sequence into a single null entry, and then take the first
// element - i.e. the first matching FacebookUser or null
let firstMatch = matchingUsers.DefaultIfEmpty().First()
// If we've not got a match, return the original user.
// Otherwise return a new copy with the appropriate avatar
select firstMatch == null ? user : user.WithAvatar(firstMatch.pic_square);
매우 약간 LINQ를 사용하는
또 다른 옵션은 비 LINQ 솔루션입니다. 예를 들어
public IEnumerable<User> MapFrom(IEnumerable<User> users)
{
var facebookUsers = GetFacebookUsers(users);
var uidDictionary = facebookUsers.ToDictionary(fb => fb.uid);
foreach (var user in users)
{
FacebookUser fb;
if (uidDictionary.TryGetValue(user.FacebookUid, out fb)
{
yield return user.WithAvatar(fb.pic_square);
}
else
{
yield return user;
}
}
}
이 대신 LINQ 쿼리 표현식 반복기 블록을 사용한다. 웹 서비스를 가정
이
private Facebook.user[] GetFacebookUsers(IEnumerable<User> users)
{
var uids = (from u in users
where u.FacebookUid != null
select u.FacebookUid.Value).Distinct().ToList();
// return facebook users for uids using WCF
}
물론, 적절하게 작동합니다 - 그것은 두 번 같은 키를 받으면 ToDictionary
이 예외가 발생합니다이 문제를 해결하기위한 하나의 옵션은 확인은 별개의 아이디를 검색 할 GetFacebookUsers
을 변경하는 것입니다 - 그렇지 않은 경우에, 당신은 아마 세에서 골라 봐 어쨌든 예외 :
결론
을 던져합니다. 그룹 조인은 아마도 이해하기가 어렵지만 가장 잘 동작합니다. 반복자 블록 솔루션은 아마도 가장 간단 할 수 있으며 GetFacebookUsers
수정으로 정상적으로 동작해야합니다.
User
을 불변으로 만드는 것은 거의 확실한 긍정적 인 단계입니다.
이러한 모든 솔루션의 좋은 부산물 중 하나는 사용자가 들어오는 순서와 똑같은 순서로 나와야한다는 것입니다. 이는 사용자에게는 중요하지 않지만 좋은 속성 일 수 있습니다. 가 돌연변이가는 방법 : -
희망하는 데 도움이 그것은 흥미로운 질문 :
EDIT이었다?
로컬 사용자 유형이 실제로 엔티티 프레임 워크에서 개체 유형입니다 귀하의 의견에 보이는 데, 행동이 과정을하기에 적합하지 않을 수 있습니다. 그것을 불변으로 만드는 것은 꽤 많은 의문의 여지가 있습니다. 그리고 저는이 타입의 대부분이 을 기대하고 있습니다. 변이를 기대합니다.
그런 경우 인터페이스를 변경하여 그 값을 명확하게 할 가치가 있습니다. 대신 (- 어느 정도 - 의미있는 돌출부)를 IEnumerable<User>
을 반환하는이 같은 당신을 떠나, 당신은 서명과 이름을 모두 변경할 수 있습니다 : 다시
public sealed class FacebookMerger : IUserMerger
{
public void MergeInformation(IEnumerable<User> users)
{
var facebookUsers = GetFacebookUsers(users);
var uidDictionary = facebookUsers.ToDictionary(fb => fb.uid);
foreach (var user in users)
{
FacebookUser fb;
if (uidDictionary.TryGetValue(user.FacebookUid, out fb)
{
user.Avatar = fb.pic_square;
}
}
}
private Facebook.user[] GetFacebookUsers(IEnumerable<User> users)
{
var uids = (from u in users
where u.FacebookUid != null
select u.FacebookUid.Value).Distinct().ToList();
// return facebook users for uids using WCF
}
}
, 이것은 "특히 아니다 LINQ-y "솔루션 (더 이상 주요 운영에 포함되지 않음)이 더 이상 필요 없습니다. 당신은 "업데이트 중"입니다.
Jon, This is awesome +1, 너의 일이 너무 좋아. 그룹화를 이해한다고 생각합니다. 그 옵션을 사용할 수도 있습니다. 그룹화 메서드를 사용하면 .Concat (NullFacebookUsers)을 호출해야합니까? 또한, 내 로컬 사용자는 EF 개체입니다, 당신은 그 좋은 복제 방법을 알고 있습니까? – bendewey
아니요, 그룹화 비트는 그 중 하나와 일치하지 않는 "비 페이스 북 사용자"를 처리하므로 DefaultIfEmpty()를 사용합니다. First(). 그리고 아니, 나는 두려워하는 EF 엔티티 복제에 대해 모른다. ( –
@bendewey :이 EF 특성을 감안할 때, 내 대답에 추가 비트를 추가했다. ( –