Segítségével Linq feltérképezésére facebook profilt a használati info

szavazat
3

Miután egy könyvet olvas LINQ Gondolkodom újra írt egy leképező osztályt írtam c # használni LINQ. Én kíváncsi vagyok, ha valaki tud adni nekem a kezét. Megjegyzés: ez egy kicsit zavaros, de a felhasználói objektum helyi felhasználó és (kisbetű) az objektum keletkezik a Facebook XSD.

eredeti Mapper

public class FacebookMapper : IMapper
{
    public IEnumerable<User> MapFrom(IEnumerable<User> users)
    {
      var facebookUsers = GetFacebookUsers(users);
      return MergeUsers(users, facebookUsers);
    }

    public 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
    }

    public IEnumerable<User> MergeUsers(IEnumerable<User> users, Facebook.user[] facebookUsers)
    {
      foreach(var u in users)
      {
        var fbUser = facebookUsers.FirstOrDefault(f => f.uid == u.FacebookUid);
        if (fbUser != null)
          u.FacebookAvatar = fbUser.pic_sqare;
      }
      return users;
    }
}

Az első két kísérlet hit falak

kísérlet 1

public IEnumerable<User> MapFrom(IEnumerable<User> users)
{
  // didn't have a way to check if u.FacebookUid == null
  return from u in users
    join f in GetFacebookUsers(users) on u.FacebookUid equals f.uid
    select AppendAvatar(u, f);
}

public void AppendAvatar(User u, Facebook.user f)
{
  if (f == null)
    return u;
  u.FacebookAvatar = f.pic_square;
  return u;
}

kísérlet 2

public IEnumerable<User> MapFrom(IEnumerable<User> users)
{
  // had to get the user from the facebook service for each single user,
  // would rather use a single http request.
  return from u in users
    let f = GetFacebookUser(user.FacebookUid)
    select AppendAvatar(u, f);
}
A kérdést 02/03/2009 20:32
a forrás felhasználó
Más nyelveken...                            


2 válasz

szavazat
5

Azt is hajlamosak, hogy írjon valamit, mint ez, hanem:

public class FacebookMapper : IMapper
{
    public IEnumerable<User> MapFacebookAvatars(IEnumerable<User> users)
    {
        var usersByID =
            users.Where(u => u.FacebookUid.HasValue)
                 .ToDictionary(u => u.FacebookUid.Value);

        var facebookUsersByID =
            GetFacebookUsers(usersByID.Keys).ToDictionary(f => f.uid);

        foreach(var id in usersByID.Keys.Intersect(facebookUsersByID.Keys))
            usersByID[id].FacebookAvatar = facebookUsersByID[id].pic_sqare;

        return users;
    }

    public Facebook.user[] GetFacebookUsers(IEnumerable<int> uids)
    {
       // return facebook users for uids using WCF
    }
}

Én azonban nem állítja, hogy volt egy nagy javulás, mid van (kivéve, ha a felhasználó vagy a facebook felhasználó gyűjtemény nagyon nagy, és ebben az esetben lehet, hogy a szél fel érezhető teljesítmény különbség.)

(Azt javasoljuk, hogy ne használja Selectki, mint egy foreachhurok, hogy végre a tényleges mutáló intézkedés egyik eleme egy sor, ahogyan te a újratervezés kísérleteket. Meg tudja csinálni, de az emberek fog lepődni a kódot, és akkor kell tartani lusta értékelés előtt tartva az egész idő alatt.)

Válaszolt 28/03/2009 22:59
a forrás felhasználó

szavazat
9

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”.

Válaszolt 01/04/2009 20:28
a forrás felhasználó

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more