Linq kullanarak benim kullanıcı bilgileri ile facebook profil eşleştirmek için

oy
3

LINQ üzerine bir kitap okuduktan sonra ben LINQ kullanmak c # yazdığı bir haritacı sınıfını yeniden yazma düşünüyorum. Herkes bana bir el verebilir, merak ediyorum. Not: onun biraz kafa karıştırıcı, ancak Kullanıcı nesnesi yerel kullanıcı ve kullanıcı (küçük) olan Facebook XSD oluşturulan bir nesnedir.

Orijinal 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;
    }
}

Benim ilk iki girişimleri duvarlara vurmak

Denemesi 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;
}

Denemesi 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);
}
Oluştur 02/03/2009 saat 20:32
kaynak kullanıcı
Diğer dillerde...                            


2 cevaplar

oy
5

Bunun yerine böyle bir şey yazmaya meyilli olacaktır:

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

Ancak, bunu ne var üzerinde büyük bir gelişme olduğunu iddia olmaz (kullanıcı veya facebook kullanıcı koleksiyonları Eğer farkedilir bir performans farkı kadar Rüzgar, bu durumda, çok büyük olmadıkça.)

(Ben kullanılmamasını öneriyoruz Selectbir benzeri foreachBunu yapabilirsin., Bir dizi bir öğesi üzerinde gerçek bir mutasyona eylemi gerçekleştirmek için döngü size üstlenmeden girişimleri yaptığı gibi, ama insanlar kodunuzda sürpriz olacak ve yeni sekme akılda bütün zaman tembel değerlendirme tutmak gerekir.)

Cevap 28/03/2009 saat 22:59
kaynak kullanıcı

oy
9

Tamam, tam olarak ne net değil IMapperonun içinde var ama başka kısıtlamalar nedeniyle mümkün olmayabilir, bazıları bir kaç şey önermek istiyorum. Bunu düşündüm ettik ben hemen hemen bu dışarı yazdım - Bunu daha kolay aynı şeyi bir dahaki sefere yapmak için yapacağız gibi, eylem düşünce dizisi görmeye yardımcı düşünüyorum. (Tabii, benim çözüm gibi varsayarsak :)

LINQ tarzında doğal olarak işlevseldir. Yani ideal olarak, sorgular yan etkilere sahip olmaması gerektiğini ifade eder. Örneğin, bir imza ile bir yöntemi beklediğiniz:

public IEnumerable<User> MapFrom(IEnumerable<User> users)

yerine mevcut kullanıcıları mutasyona daha, ekstra bilgiler içeren kullanıcı nesnelerinin yeni dizisi dönmek için. Şu anda ekliyorsanız tek bilgi avatar, o yüzden de bir yöntem eklersiniz Userçizgisinde:

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

Hatta yapmak isteyebilirsiniz Usertamamen değişmez - Böyle oluşturucu deseni olarak o etrafında çeşitli stratejiler vardır. Daha fazla ayrıntı istiyorsanız sor. Neyse, önemli olan biz eskisinin kopyası olan yeni bir kullanıcı oluşturulur, ancak belirtilen avatarınızla olduğun.

İlk denemesi: iç birleşim

Şimdi geri Eşleştiricisi ... şu anda üç var kamu yöntemleri ama benim tahminim sadece ilki kamu olması gerektiğini ve API geri kalanı aslında Facebook kullanıcılarını maruz gerek olmamasıdır. Senin gibi görünüyor GetFacebookUsersyöntem temelde tamam ben muhtemelen boşluk açısından sorgu sıraya ediyorum rağmen.

Yani, yerel kullanıcılar bir dizi ve Facebook kullanıcılarının bir koleksiyon göz önüne alındığında, biz gerçek haritalama biraz yapıyor sol ediyoruz. Bir eşleştirme Facebook kullanıcısı olmayan yerel kullanıcıların vermez çünkü düz "katılmak" şartı, problemlidir. Bunun yerine, onlar bir avatar olmadan bir Facebook kullanıcı sanki olmayan bir Facebook kullanıcısı tedavi bazı yol gerekir. Esas olarak, bu boş nesne desenidir.

Biz boş uid sahip bir Facebook kullanıcısı ile gelerek bunu yapabilir (nesne modelini varsayarak olduğunu verir):

// Adjust for however the user should actually be constructed.
private static readonly FacebookUser NullFacebookUser = new FacebookUser(null);

Ancak, biz aslında bir istiyoruz dizisi bu ne çünkü bu kullanıcıların Enumerable.Concatkullanır:

private static readonly IEnumerable<FacebookUser> NullFacebookUsers =
    Enumerable.Repeat(new FacebookUser(null), 1);

Şimdi biz sadece bizim gerçek birine bu kukla girdi "add" ve normal bir iç birleşim yapabilirsiniz. Bu unutmayın varsayar Facebook kullanıcı arama daima herhangi bir "gerçek" Facebook UID için bir kullanıcıyı bulacaktır. Eğer durum değilse, bu tekrar ve bir iç birleşim kullanmayın gerekirdi.

Sonra katılmak ve proje kullanarak bunu, sonunda "sıfır" kullanıcı şunlardır 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);
}

Yani tam sınıf olacaktır:

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

Burada birkaç nokta:

  • önce belirtildiği gibi, iç bir kullanıcının Facebook UID geçerli bir kullanıcı olarak getirilen olmayabilir eğer sorunlu hale katılmak.
  • yinelenen Facebook kullanıcı varsa Benzer sorunlarımız olsun - her yerel kullanıcı iki kez çıkan sona ereceğini!
  • Bu sigara Facebook kullanıcıları için avatar (silen) yerini alır.

İkinci bir yaklaşım: grup katılması

Biz bu noktaları ele alabilir Bakalım. Bence getirilen ettik eğer farz edeceğiz birden tek bir Facebook UID için Facebook kullanıcılarını, o zaman biz dan avatar yakala bunlardan hangisinin önemi yoktur - onlar aynı olmalıdır.

İhtiyacımız her yerel kullanıcı için biz Facebook kullanıcıları eşleşen bir dizi olsun, böylece bir grup, katılmak olduğunu. Sonra kullanacağız DefaultIfEmptyhayatı kolaylaştırmak için.

Biz tutabilir WithAvatareskisi gibi - ama bu sefer biz sadece gelen avatar kapmak için bir Facebook kullanıcısı var eğer bunu arayacağız. Bir grup tarafından temsil edilmektedir C # sorgu ifadeleri katılmak join ... into. Bu sorgu makul uzun, ama dürüst, çok korkutucu değil!

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);
}

İşte sorgu ifadesi yine, ama yorumlarla:

// "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);

olmayan LINQ çözeltisi

Başka bir seçenek olarak sadece biraz LINQ kullanmaktır. Örneğin:

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

Bu, bir yineleyici blok yerine LINQ sorgu ifade kullanır. ToDictionaryiki kez aynı anahtarı alırsa bir istisna durumu - Bu sorunu gidermek için bir seçenek değiştirmektir GetFacebookUsersemin sadece farklı kimlikleri arar hale getirmek için:

    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
    }

Bu web hizmeti elbette uygun şekilde çalışır varsayar - ama öyle değil, muhtemelen yine bir özel durum istiyoruz :)

Sonuç

Üç takım tercihinizi yapın. Grup katılmak anlamak muhtemelen en zor, ama en iyi davranır. Yineleyici blok çözeltisi muhtemelen en basit ve uyar davranması gerektiğini GetFacebookUsersmodifikasyonu.

Yapımı Userdeğişmez neredeyse kesin olsa olumlu bir adım olacaktır.

yan ürün Bu çözümlerin her biri güzel O kadar da iyi sizin için önemli olmayabilir. kullanıcılar içeri girdiler aynı sırada çıkıp yani, ama güzel bir özellik olabilir.

Umarım bu yardımcı olur - bu ilginç bir soru oldu :)

DÜZENLEME: mutasyon gitmek için yolu var mı?

Yerel Kullanıcı tipi aslında varlık çerçevesinde bir varlık türü olduğunu yorumlarınızda gördükten, o olabilir eylem bu kursu almak uygun olmayabilir. Iletmenin yapma hemen hemen söz konusu değildir, ben tip birçok kullanımı olacağını sanıyorum bekliyoruz mutasyonu.

Eğer durum buysa, daha açık olmasını sağlamak için olabildiğince arayüzünü değiştirmeye değer olabilir. Bunun yerine bir dönüş IEnumerable<User>(- bir ölçüde - ima hangi projeksiyon) böyle bir şey ile ayrılırken, sen imza ve isim hem değiştirmek isteyebilirsiniz:

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

; Ama sen gerçekten "sorgulama" konum olarak o, makul - Yine, bu (ana operasyonda) özellikle "LINQ-y" çözüm artık değil Eğer "güncelleme" ediyoruz.

Cevap 01/04/2009 saat 20:28
kaynak kullanıcı

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