LINQ का उपयोग कर मेरे उपयोगकर्ता की जानकारी के साथ फेसबुक प्रोफाइल मैप करने के लिए

वोट
3

LINQ पर एक किताब को पढ़ने के बाद मैं एक नक्शाकार वर्ग है कि मैं सी # में लिखा था LINQ उपयोग करने के लिए फिर से लिखने के बारे में सोच रहा हूँ। अगर कोई मुझे एक हाथ दे सकते हैं मैं सोच रहा हूँ। नोट: इसके थोड़ा भ्रामक है, लेकिन उपयोगकर्ता वस्तु स्थानीय उपयोगकर्ता और उपयोगकर्ता (लोअरकेस) है फेसबुक XSD से उत्पन्न वस्तु है।

मूल मैपर

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

मेरी पहली दो प्रयास दीवारों मारा

प्रयास 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;
}

प्रयास 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);
}
02/03/2009 को 20:32
का स्रोत उपयोगकर्ता
अन्य भाषाओं में...                            


2 जवाब

वोट
5

मैं इस के बजाय की तरह कुछ लिखने के लिए इच्छुक हो जाएगा:

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

हालांकि, मैं यह दावा नहीं है कि आप क्या मिल गया है पर एक बड़ा सुधार था (जब तक उपयोगकर्ता या फेसबुक उपयोगकर्ता संग्रह बहुत बड़ी है, जो मामले में आप एक उल्लेखनीय प्रदर्शन अंतर के साथ हवा हो सकती है।)

(मैं का उपयोग कर के खिलाफ सलाह देते हैं Selectएक तरह foreachपाश एक सेट का एक तत्व पर एक वास्तविक परिवर्तनशील क्रिया करने के लिए, जिस तरह से आप अपने रिफैक्टरिंग प्रयास में किया था। आप यह कर सकते हैं, लेकिन लोगों को अपने कोड से हैरान हो जाएगा, और आप करेंगे मन में आलसी मूल्यांकन पूरे समय रखना है।)

28/03/2009 को 22:59
का स्रोत उपयोगकर्ता

वोट
9

ठीक है, यह स्पष्ट नहीं है कि वास्तव में क्या IMapperउस में है, लेकिन मैं कुछ चीजें, जिनमें से कुछ अन्य प्रतिबंधों के कारण संभव नहीं हो सकता है सुझाव देना चाहेंगे। मैं बहुत ज्यादा यह पता लिखा है के रूप में मैं इसके बारे में सोचा है - मैं इसे कार्रवाई में सोच की धारा को देखने के लिए मदद करता है, के रूप में यह आसान आप एक ही बात अगली बार करने के लिए बनाती हूँ लगता है। (यह मानते हुए कि आप मेरे समाधान चाहते, ज़ाहिर है :)

LINQ शैली में स्वाभाविक कार्यात्मक है। इसका मतलब है कि आदर्श, प्रश्नों दुष्प्रभाव नहीं होना चाहिए। उदाहरण के लिए, मैं के हस्ताक्षर के साथ एक विधि उम्मीद थी:

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

बल्कि मौजूदा उपयोगकर्ताओं परिवर्तनशील से अतिरिक्त जानकारी के साथ उपयोगकर्ता वस्तुओं की एक नया अनुक्रम वापस जाने के लिए,। केवल जानकारी आप वर्तमान में जोड़ रहे हैं अवतार है, इसलिए मैं में एक विधि जोड़ना होगा 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;
}

तुम भी बनाने के लिए चाहते हो सकता है Userपूरी तरह से अडिग - जैसे बिल्डर पैटर्न के रूप में है कि चारों ओर विभिन्न रणनीतियों, देखते हैं। मुझे आप से पूछें कि आप अधिक विवरण चाहते हैं। वैसे भी, मुख्य बात यह है कि हम जो पुराने एक की एक प्रति है एक नया उपयोगकर्ता बना लिया है, लेकिन निर्दिष्ट अवतार के साथ है।

पहला प्रयास: भीतरी में शामिल होने के

अब वापस अपने नक्शाकार करने के लिए ... आप वर्तमान में तीन मिल गया है सार्वजनिक तरीकों लेकिन मेरे अनुमान है कि केवल पहले एक सार्वजनिक होना चाहिए, और एपीआई के बाकी वास्तव में फेसबुक उपयोगकर्ताओं को बेनकाब करने की जरूरत नहीं है कि है। ऐसा लगता है कि अपने GetFacebookUsersविधि मूल रूप से ठीक है, हालांकि मैं शायद खाली स्थान के के मामले में क्वेरी को पंक्तिबद्ध चाहते हैं।

तो, स्थानीय उपयोगकर्ताओं के अनुक्रम और फेसबुक उपयोगकर्ताओं का एक संग्रह को देखते हुए, हम वास्तविक मानचित्रण बिट कर जाते हैं। क्योंकि यह स्थानीय उपयोगकर्ताओं जो एक मिलान फेसबुक उपयोगकर्ता की जरूरत नहीं है उत्पन्न नहीं करेंगे एक सीधे "में शामिल होने" खंड, समस्याग्रस्त है। इसके बजाय, हम एक गैर Facebook उपयोगकर्ता के इलाज के रूप में यदि वे एक अवतार के बिना एक Facebook उपयोगकर्ता थे की किसी तरह की जरूरत है। अनिवार्य रूप से इस अशक्त वस्तु पैटर्न है।

हम ऐसा कर सकते हैं एक Facebook उपयोगकर्ता जो एक अशक्त 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);

अब हम केवल हमारे असली को यह डमी प्रविष्टि "जोड़ें" कर सकते हैं, और एक सामान्य भीतरी में शामिल होने से करते हैं। नोट: यह है कि मान लिया गया है कि फेसबुक उपयोगकर्ताओं के देखने हमेशा किसी भी "असली" फेसबुक यूआईडी के लिए एक उपयोगकर्ता मिल जाएगा। अगर ऐसा नहीं है, हम इस पर दोबारा जाने और एक आंतरिक में शामिल होने का उपयोग नहीं करना होगा।

हम तो शामिल होते हैं और इस परियोजना का उपयोग कर, अंत में "अशक्त" उपयोगकर्ता शामिल 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
    }
}

यहाँ कुछ अंक:

  • जैसा कि पहले बताया गया है, भीतरी में शामिल होने के समस्याग्रस्त हो जाता है यदि कोई उपयोगकर्ता के फेसबुक यूआईडी मान्य उपयोगकर्ता के रूप में प्राप्त नहीं किया जा सकता है।
  • इसी तरह हम समस्याओं मिल अगर हम डुप्लीकेट फेसबुक उपयोगकर्ताओं है - प्रत्येक स्थानीय उपयोगकर्ता दो बार बाहर आ रहा अंत होगा!
  • यह बदल देता है (को हटाता है) गैर फेसबुक उपयोगकर्ताओं के लिए अवतार।

एक दूसरा दृष्टिकोण: समूह में शामिल होने

देखते हैं कि हम इन बातों का समाधान कर सकते हैं या नहीं। मैं मान लेंगे कि अगर हम लाए जाने है कई एक भी फेसबुक यूआईडी के लिए फेसबुक उपयोगकर्ताओं है, तो यह कोई फर्क नहीं पड़ता जो उनमें से हम से अवतार हड़पने - वे एक ही होना चाहिए।

क्या हम की जरूरत है एक समूह में शामिल करें, ताकि प्रत्येक स्थानीय उपयोगकर्ता के लिए हम फेसबुक उपयोगकर्ताओं मिलान के एक दृश्य मिलता है। हम तो इस्तेमाल करेंगे DefaultIfEmptyजीवन को आसान बनाने के लिए।

हम रख सकते WithAvatarके रूप में यह पहले था - लेकिन इस बार हम केवल यह कॉल करने के लिए हम से अवतार हड़पने के लिए एक Facebook उपयोगकर्ता मिल गया है, तो जा रहे हैं। एक समूह का प्रतिनिधित्व करती है सी # क्वेरी भाव में शामिल होने के 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;
        }
    }
}

यह एक iterator ब्लॉक के बजाय एक LINQ क्वेरी अभिव्यक्ति का उपयोग करता है। ToDictionaryअगर यह एक ही कुंजी को दो बार प्राप्त करता है एक अपवाद फेंक होगा - यह आस-पास काम करने के लिए एक विकल्प बदलने के लिए है GetFacebookUsersयकीन है कि यह केवल अलग आईडी के लिए लग रहा है बनाने के लिए:

    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
    }

यही कारण है कि मान लिया गया है वेब सेवा उचित रूप से काम करता है, निश्चित रूप से - लेकिन अगर ऐसा नहीं होता है, तो आप शायद वैसे भी एक अपवाद फेंक करना चाहते हैं :)

निष्कर्ष

तीन में से अपने पसंद है। समूह में शामिल होने शायद समझने के लिए सबसे मुश्किल है, लेकिन सबसे अच्छा व्यवहार करता है। Iterator ब्लॉक समाधान संभवतः सबसे सरल है, और साथ ठीक से व्यवहार करना चाहिए GetFacebookUsersसंशोधन।

बनाना Userअपरिवर्तनीय लगभग निश्चित रूप से एक सकारात्मक कदम है, हालांकि होगा।

उप-उत्पाद के इन समाधानों में से सभी में से एक अच्छा है कि उपयोगकर्ताओं को एक ही आदेश है कि वे में चला गया में बाहर आ रहा है। यही कारण है कि अच्छी तरह से नहीं आप के लिए महत्वपूर्ण हो सकता है, लेकिन यह एक अच्छा संपत्ति हो सकती है।

आशा है कि इस मदद करता है - यह एक दिलचस्प सवाल हो गया है :)

संपादित करें: उत्परिवर्तन जाने का रास्ता है?

अपनी टिप्पणी में देखने के बाद कि स्थानीय उपयोगकर्ता प्रकार वास्तव में इकाई की रूपरेखा से एक इकाई प्रकार है, यह हो सकता है कार्रवाई के इस पाठ्यक्रम लेने के लिए उपयुक्त नहीं हो। यह अपरिवर्तनीय बनाना काफी सवाल से बाहर है, और मुझे लगता है कि प्रकार के ज़्यादातर उपयोग होगा उम्मीद उत्परिवर्तन।

अगर ऐसी बात है, तो यह आपके इंटरफेस बदल रहा है कि स्पष्ट करने के लायक हो सकता है। इसके बजाय लौटने एक के 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" समाधान (मुख्य संचालन में) किसी भी अधिक नहीं है - लेकिन यह है कि उचित है, जैसा कि आप वास्तव में नहीं "क्वेरी" कर रहे हैं; आप "अद्यतन करने" कर रहे हैं।

01/04/2009 को 20:28
का स्रोत उपयोगकर्ता

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