c# – 使用Linq将facebook个人资料与我的用户信息进行映射
在阅读了有关LINQ的书之后,我正在考虑重写我在c#中编写的mapper类以使用LINQ.我想知道是否有人能帮助我.注意:它有点令人困惑,但User对象是本地用户,而用户(小写)是从Facebook XSD生成的对象.
原始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; } } 我的前两次尝试击中了墙壁 尝试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); } 解决方法
好吧,目前还不清楚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; } 您甚至可能希望使用户完全不可变 – 有各种策略,例如构建器模式.问我是否想要更多细节.无论如何,最重要的是我们创建了一个新用户,它是旧用户的副本,但具有指定的头像. 第一次尝试:内部联接 现在回到你的mapper ……你现在有三个公共方法,但我的猜测是只有第一个需要公开,而其余的API实际上并不需要暴露Facebook用户.看起来你的GetFacebookUsers方法基本没问题,虽然我可能会根据空白排列查询. 因此,给定一系列本地用户和一组Facebook用户,我们将留下实际的映射位.直接的“join”子句是有问题的,因为它不会产生没有匹配的Facebook用户的本地用户.相反,我们需要某种方式来对待非Facebook用户,就好像他们是没有头像的Facebook用户一样.基本上这是空对象模式. 我们可以通过提出一个具有null uid的Facebook用户来做到这一点(假设对象模型允许): // 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的用户.如果情况并非如此,我们需要重新审视这一点,而不是使用内连接. 我们在最后包含“null”用户,然后使用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 UID获取了多个Facebook用户,那么我们从哪个获取头像并不重要 – 它们应该是相同的. 我们需要的是一个群组加入,以便为每个本地用户提供一系列匹配的Facebook用户.然后我们将使用DefaultIfEmpty来简化生活. 我们可以像往常一样保持WithAvatar – 但是这次我们只会打电话给它,如果我们有Facebook用户从中获取头像. 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; } } } 这使用迭代器块而不是LINQ查询表达式.如果ToDictionary两次收到相同的密钥,它将抛出异常 – 解决此问题的一个选项是更改GetFacebookUsers以确保它只查找不同的ID: 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 } 这当然假设Web服务正常工作 – 但如果没有,你可能想要抛出异常:) 结论 从三者中挑选出来.小组加入可能最难理解,但行为最好.迭代器块解决方案可能是最简单的,并且应该对GetFacebookUsers修改表现良好. 让用户一成不变几乎肯定是一个积极的步骤. 所有这些解决方案的一个不错的副产品是用户以他们进入的顺序出现.这对您来说可能并不重要,但它可能是一个不错的属性. 希望这会有所帮助 – 这是一个有趣的问题:) 编辑:突变是怎么回事? 在您的评论中看到本地用户类型实际上是实体框架中的实体类型,采取这种行动可能不合适.使其不可变是非常不可能的,我怀疑该类型的大多数用途都会产生变异. 如果是这种情况,可能值得更改您的界面以使其更清晰.而不是返回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”解决方案(在主要操作中) – 但这是合理的,因为你并不是真的“查询”;你正在“更新”. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- ruby – 如何以编程方式确定哪个类/模块定义了被调用的方法
- FAIL - Context /SsmApp is defined in server.xml and may
- swift – 逻辑运算符的类型是什么?
- Study Flex《用MXML表示ActionScript对象》
- WebWork配置文件详解
- react-native – 如何在本机中将scrollview拉伸到整个屏幕?
- u-boot-2014.10移植第24天----nand flash启动(二)
- objective-c – 在升级到Xcode 7.3 swift 2.2之后,从swift调
- C. Alyona and the Tree
- c# – 在同一站点上从一个页面到另一个页面执行Response.Re