Εντάξει, δεν είναι σαφές τι ακριβώς IMapperέχει σ 'αυτό, αλλά εγώ θα πρότεινα κάποια πράγματα, μερικά από τα οποία μπορεί να μην είναι εφικτή λόγω άλλων περιορισμών. Έχω γράψει αυτό έξω λίγο πολύ όπως έχω σκεφτεί αυτό - νομίζω ότι βοηθάει να δούμε το τρένο της σκέψης σε δράση, καθώς αυτό θα καταστήσει ευκολότερο για εσάς να κάνετε το ίδιο πράγμα την επόμενη φορά. (Υποθέτοντας ότι σας αρέσει λύσεις μου, φυσικά :)
LINQ είναι εγγενώς λειτουργικό στιλ. Αυτό σημαίνει ότι στην ιδανική περίπτωση, τα ερωτήματα δεν πρέπει να έχει παρενέργειες. Για παράδειγμα, θα περίμενε κανείς μια μέθοδο με την υπογραφή του:
public IEnumerable<User> MapFrom(IEnumerable<User> users)
να επιστρέψει μια νέα σειρά από αντικείμενα χρήστη με έξτρα πληροφορίες, παρά τη μετάλλαξή τους υπάρχοντες χρήστες. Η μόνη πληροφορία που είστε σήμερα προσαρτώντας είναι το avatar, οπότε θα ήθελα να προσθέσω μια μέθοδο 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πλήρη αμετάβλητο - υπάρχουν διάφορες στρατηγικές γύρω από αυτό, όπως το μοτίβο οικοδόμος. Ρώτα με αν θέλετε περισσότερες λεπτομέρειες. Τέλος πάντων, το κυριότερο είναι ότι έχουμε δημιουργήσει ένα νέο χρήστη που είναι αντίγραφο της παλιάς, αλλά με την καθορισμένη avatar.
Πρώτη προσπάθεια: εσωτερικός σύνδεσμος
Τώρα πίσω στο mapper σας ... έχετε σήμερα πήρε τρεις δημόσιες μεθόδους, αλλά μου εικασία είναι ότι μόνο το πρώτο πρέπει να είναι δημόσια, και ότι το υπόλοιπο της API δεν πρέπει πραγματικά να εκθέσει τους χρήστες του Facebook. Μοιάζει με σας GetFacebookUsersμέθοδος είναι ουσιαστικά εντάξει, αν και μάλλον θα παρατάξει το ερώτημα όσον αφορά την κενό.
Έτσι, δίνεται μια ακολουθία των τοπικών χρηστών και μια συλλογή των χρηστών του Facebook, είμαστε άφησε να κάνει το πραγματικό κομμάτι χαρτογράφησης. Μια ευθεία ρήτρα «ενταχθούν» είναι προβληματική, επειδή δεν θα αποφέρει τα τοπικά χρήστες που δεν έχουν αντιστοιχία των χρηστών του Facebook. Αντ 'αυτού, χρειαζόμαστε κάποιο τρόπο για τη θεραπεία ενός χρήστη που δεν το Facebook σαν να ήταν ένας χρήστης του Facebook, χωρίς ένα avatar. Ουσιαστικά αυτή είναι η μηδενική μοτίβο αντικείμενο.
Μπορούμε να το κάνουμε αυτό με καταλήξουμε σε ένα χρήστη του Facebook που έχει uid null (υποθέτοντας ότι το μοντέλο αντικειμένου επιτρέπει αυτό):
// 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. Αν αυτό δεν είναι η περίπτωση, θα πρέπει να επανεξετάσουμε αυτό και να μην χρησιμοποιούν ένα εσωτερικό ενταχθούν.
Θα περιλαμβάνει το «μηδέν» του χρήστη στο τέλος, στη συνέχεια, κάντε τα ενταχθεί και το έργο με τη χρήση 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 - κάθε τοπικός χρήστης θα καταλήξει να βγαίνουν δύο φορές!
- Αυτό αντικαθιστά (απομακρύνει) το avatar για τους χρήστες που δεν του Facebook.
Μια δεύτερη προσέγγιση: Ομάδα ενταχθούν
Ας δούμε αν μπορούμε να αντιμετωπίσουμε αυτά τα σημεία. Θα υποθέσουμε ότι αν έχουμε παρατραβηγμένο πολλούς χρήστες του Facebook για μια ενιαία Facebook UID, τότε δεν έχει σημασία ποια από αυτά θα αρπάξει το avatar από - θα πρέπει να είναι η ίδια.
Αυτό που χρειαζόμαστε είναι μια ομάδα ενταχθεί, έτσι ώστε για κάθε τοπικό χρήστη παίρνουμε μια ακολουθία που ταιριάζουν χρηστών του Facebook. Στη συνέχεια, θα χρησιμοποιούν DefaultIfEmptyγια να κάνουν τη ζωή ευκολότερη.
Μπορούμε να κρατήσει WithAvatarόπως ήταν πριν - αλλά αυτή τη φορά είμαστε μόνο για να το ονομάσουμε, αν έχουμε ένα χρήστη του Facebook για να αρπάξει το avatar από το. Μια ομάδα ενταχθούν σε 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;
}
}
}
Αυτό χρησιμοποιεί ένα μπλοκ 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αμετάβλητο σχεδόν βέβαιο ότι θα είναι ένα θετικό βήμα, όμως.
Ένα ωραίο υποπροϊόν όλων αυτών των λύσεων είναι ότι οι χρήστες βγαίνουν με την ίδια σειρά πήγαν στο. Αυτό μπορεί να μην είναι σημαντικό για εσάς, αλλά μπορεί να είναι ένα ωραίο ξενοδοχείο.
Η ελπίδα αυτό βοηθά - αυτό είναι μια ενδιαφέρουσα ερώτηση :)
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» λύση (στην κύρια λειτουργία) πια - αλλά αυτό είναι λογικό, καθώς δεν είστε πραγματικά «επερώτηση»? είστε «ενημέρωση».