Oké, ez nem világos, hogy pontosan mi IMappervan benne, de én azt javaslom, egy pár dolgot, amelyek közül néhány nem megvalósítható, mivel egyéb korlátozásokat. Azért írtam ezt, elég sok, mint gondoltam róla - azt hiszem, ez segít, hogy a gondolatmenet akcióban, mint hogy megpróbáljuk könnyebbé teszi, hogy nem ugyanaz a dolog legközelebb. (Feltételezve, hogy tetszik a megoldás, persze :)
LINQ eredendően funkcionális stílusban. Ez azt jelenti, hogy ideális lekérdezések nem kell mellékhatásokkal. Például, azt várják a módszer aláírással:
public IEnumerable<User> MapFrom(IEnumerable<User> users)
hogy visszatérjen egy új sorozata a felhasználói objektumok extra információkat, hanem mutálódik a meglévő felhasználók számára. Az csak akkor éppen mellékelik az avatar, úgyhogy hozzá egy módszert Usermentén:
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;
}
Lehet, hogy még szeretne Userteljesen megváltoztathatatlan - vannak különböző stratégiák körül, hogy például az építő alakzatot. Kérdezd meg, ha többet szeretne részleteket. Mindegy, a lényeg az, hogy már létrehozott egy új felhasználót, amely egy példányt a régit, de a megadott avatar.
Az első kísérlet: inner join
Most vissza a leképező ... amit jelenleg is három állami módszereket, de én hiszem , hogy csak az elsőt kell lennie az állami, valamint hogy a többi API valójában nem kell, hogy ki a Facebook felhasználók számára. Úgy tűnik, a GetFacebookUsersmódszer alapvetően rendben van, bár én valószínűleg sorakoznak a lekérdezés szempontjából szóközöket.
Tehát, mivel a szekvencia a helyi felhasználók és a gyűjtemény a Facebook felhasználók számára, mi maradunk ezzel a tényleges térképezési kicsit. Egy egyenes „csatlakozni” klauzula problematikus, mert nem járna a helyi felhasználók, amelyek nem rendelkeznek a megfelelő Facebook felhasználó. Ehelyett meg kell valamilyen módon kezeljük, nem Facebook felhasználói mintha egy Facebook-felhasználó nincsen avatar. Lényegében ez a null objektum mintát.
Meg tudjuk csinálni, hogy ha jön egy Facebook-felhasználó, aki egy null uid (feltételezve, hogy az objektum modell lehetővé teszi, hogy a):
// Adjust for however the user should actually be constructed.
private static readonly FacebookUser NullFacebookUser = new FacebookUser(null);
Azonban mi valóban szeretnénk egy szekvencia ezek a felhasználók, mert ez az, amit Enumerable.Concathasznál:
private static readonly IEnumerable<FacebookUser> NullFacebookUsers =
Enumerable.Repeat(new FacebookUser(null), 1);
Most egyszerűen az „add” ez dummy bejegyzés a mi igazi, és nem egy normális belső csatlakozhat. Megjegyezzük, hogy ez azt feltételezi , hogy a keresés a Facebook felhasználók mindig megtalálja a felhasználó minden „igazi” Facebook UID. Ha ez nem így lenne, akkor lenne szükség, hogy újra ezt, és nem használja a belső csatlakozhat.
Mi is a „null” felhasználó a végén, akkor ne a csatlakozásra és a projekt segítségével 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);
}
Így a teljes osztályban lenne:
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
}
}
Néhány pont van:
- Amint azt korábban, a belső összekapcsolás problematikussá válik, ha a felhasználó Facebook UID esetleg nem túlzás a felhasználó érvényes.
- Hasonlóan vannak problémák, ha vannak ismétlődő Facebook felhasználók - Minden felhasználó a végén jön ki kétszer!
- Ez helyettesíti (eltávolítja) az avatar nem Facebook felhasználók számára.
Egy másik megközelítés: csoport csatlakozik
Lássuk, mi ezekkel a pontokat. Feltételezem, hogy ha már letöltött több Facebook felhasználó Egyetlen Facebook UID, akkor nem számít, hogy melyik közülük megragad a avatar - ők meg kell egyeznie.
Amire szükségünk van egy csoport csatlakozik úgy, hogy az egyes helyi felhasználói jutunk sorozata illő Facebook felhasználók. Majd utána DefaultIfEmpty, hogy az élet könnyebb.
Tudjuk tartani WithAvatar, mint előtte volt - de ezúttal mi csak fog hívni, ha megvan egy Facebook-felhasználó, hogy megragad a avatar. A csoport csatlakozik a C # lekérdezés kifejezések jelentése join ... into. Ez a lekérdezés meglehetősen hosszú, de ez nem túl ijesztő, becsületes!
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);
}
Itt a keresett kifejezést újra, de megjegyzésekkel:
// "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);
A nem-LINQ megoldás
Egy másik lehetőség az, hogy csak nagyon kis mértékben LINQ. Például:
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;
}
}
}
Ez használ egy bejáró blokk helyett LINQ lekérdezés kifejezés. ToDictionaryfog dobni egy kivételt, ha megkapja ugyanazt a kulcsot kétszer - az egyik lehetőség a munka körül ez a változtatás GetFacebookUsers, hogy megbizonyosodjon arról, hogy néz ki, csak a különböző azonosítók:
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
}
Ez feltételezi, hogy a webes szolgáltatás működik megfelelően, természetesen -, de ha nem, akkor valószínűleg szeretne dobni egy kivételt egyébként :)
Következtetés
Nem kell választanod a háromból. A csoport csatlakozik talán legnehezebb megérteni, de úgy viselkedik a legjobban. A bejáró blokk megoldás talán a legegyszerűbb, és úgy kell viselkedniük, rendben a GetFacebookUsersmódosítást.
Így Usermegváltoztathatatlan szinte biztosan pozitív lépés mégis.
Egy szép melléktermék mindezen megoldások az, hogy a felhasználók jön ki ugyanabban a sorrendben, ahogy beléptek. Ez bizonyára nem fontos neked, de ez lehet egy szép tulajdonság.
Remélem ez segít - ez egy érdekes kérdés :)
EDIT: A mutáció az út?
Látva a megjegyzéseket, hogy a helyi Felhasználó típus valójában egy entitás típusát a szervezet keretében, akkor lehet, nem lenne helyénvaló, hogy ezt a lépést. Ezzel megváltoztathatatlan nagyjából ki a kérdés, és az a gyanúm, hogy a legtöbb felhasználási típusú fog várni mutációt.
Ha ez a helyzet, akkor lehet, hogy érdemes változik a felület, hogy az egyértelmű. Ahelyett, hogy visszatért egy IEnumerable<User>(amely magában foglalja - bizonyos mértékig - vetítés) érdemes változtatni mind az aláírást, és a nevét, így ha valami ilyesmit:
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
}
}
Ismét, ez nem egy különösen „LINQ-y” oldat (a fő művelet) tovább - de ez azért indokolt, te nem igazán „lekérdező”; te „frissítése”.