我在 Azure Web角色上托管了无状态后端服务,该服务查询实体框架容器等。客户端使用 WCF 和wsHttpBinding
与之进行通信。 客户端在调用某些方法时会从后端收到超时,即使我在某些情况下解决了这个问题,但我似乎无法查明根本原因。
这是我目前所知道的:
IEnumerable<T>
从LINQ查询获得的结果,以及MembershipUserCollection
。.ToList()
添加到要返回的集合中。这是失败的方法之一:
WCF界面:
[OperationContract(Action = "http://tempuri.org/IBackendService/GetAllUsers", ReplyAction = "http://tempuri.org/IBackendService/GetAllUsersResponse")]
MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords);
WCF实施(简化):
protected UserRepository Users;
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
// Get all users in the page range specified.
var userEntities = Users.GetAll();
var users = new MembershipUserCollection();
userEntities = userEntities.GetPagedRange(pageIndex, pageSize).ToList();
userEntities.ForEach(ue => users.Add(Mappings.Map<UserEntity, User>(ue)));
totalRecords = userEntities.Count();
return users;
}
映射是一个使用AutoMapper定义数据和业务对象之间的映射规则的类。 User
继承自MembershipUser
。以下是与此相关的内容:(WIP。请原谅这一切。)
Mapper.CreateMap<UserEntity, User>()
.ConstructUsing(ue => new User
(
ue.UserId,
ue.ProviderName,
ue.UserName,
ue.Email,
ue.PasswordQuestion,
ue.IsApproved,
ue.IsLockedOut,
ue.CreationDate,
ue.LastLoginDate.HasValue ? ue.LastLoginDate.Value : DateTime.MinValue,
ue.LastActivityDate.HasValue ? ue.LastActivityDate.Value : DateTime.MinValue,
ue.LastPasswordChangedDate.HasValue ? ue.LastPasswordChangedDate.Value : DateTime.MinValue,
ue.LastLockoutDate.HasValue ? ue.LastLockoutDate.Value : DateTime.MinValue,
ue.Comment,
ue.Customers.Select(Map<CustomerEntity, Customer>).ToList(),
ue.Roles.Select(Map<RoleEntity, Role>).ToList(),
ue.Roles.SelectMany(r => r.Activities).Select(Map<ActivityEntity, Activity>).ToList()
))
.IgnoreAllNonExisting();
Mapper.CreateMap<User, UserEntity>()
.ForMember(ue => ue.Comment, opt => opt.MapFrom(u => u.Comment))
.ForMember(ue => ue.CreationDate, opt => opt.MapFrom(u => u.CreationDate))
.ForMember(ue => ue.Email, opt => opt.MapFrom(u => u.Email))
.ForMember(ue => ue.IsApproved, opt => opt.MapFrom(u => u.IsApproved))
.ForMember(ue => ue.IsLockedOut, opt => opt.MapFrom(u => u.IsLockedOut))
.ForMember(ue => ue.LastActivityDate, opt => opt.MapFrom(u => u.LastActivityDate.Equals(DateTime.MinValue) ? (DateTime?)null : u.LastActivityDate))
.ForMember(ue => ue.LastLockoutDate, opt => opt.MapFrom(u => u.LastLockoutDate.Equals(DateTime.MinValue) ? (DateTime?)null : u.LastLockoutDate))
.ForMember(ue => ue.LastLoginDate, opt => opt.MapFrom(u => u.LastLoginDate.Equals(DateTime.MinValue) ? (DateTime?)null : u.LastLoginDate))
.ForMember(ue => ue.LastPasswordChangedDate,
opt => opt.MapFrom(u => u.LastPasswordChangedDate.Equals(DateTime.MinValue) ? (DateTime?)null : u.LastPasswordChangedDate))
.ForMember(ue => ue.PasswordQuestion, opt => opt.MapFrom(u => u.PasswordQuestion))
.ForMember(ue => ue.ProviderName, opt => opt.MapFrom(u => u.ProviderName))
.ForMember(ue => ue.UserId, opt => opt.MapFrom(u => (int)u.ProviderUserKey))
.ForMember(ue => ue.UserName, opt => opt.MapFrom(u => u.UserName))
.ForMember(ue => ue.Password, opt => opt.Ignore())
.ForMember(ue => ue.PasswordAnswer, opt => opt.Ignore())
.ForMember(ue => ue.ApplicationName, opt => opt.Ignore())
.ForMember(ue => ue.Roles, opt => opt.Ignore())
.ForMember(ue => ue.Customers, opt => opt.Ignore())
.IgnoreAllNonExisting();
IgnoreAllNonExisting 是一种扩展方法:
public static IMappingExpression<TSource, TDestination> IgnoreAllNonExisting<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var sourceType = typeof(TSource);
var destinationType = typeof(TDestination);
var existingMaps = Mapper.GetAllTypeMaps().First(x => x.SourceType == sourceType && x.DestinationType == destinationType);
foreach (var property in existingMaps.GetUnmappedPropertyNames())
{
expression.ForMember(property, opt => opt.Ignore());
}
return expression;
}
我最好的猜测是,这是WCF层中的序列化问题,但我不知道可能触发它的是什么。除此之外, 我完全无能为力。
我还应该检查什么?您还需要更多信息吗?
编辑:添加了moar片段以便澄清。
答案 0 :(得分:1)
看起来我终于找到了答案。
似乎MembershipUser
类根本不可序列化,即使它标有Serializable
属性。根据{{3}},它还标有SecurityPermission
属性,出于安全原因禁止序列化。
标记一个具有矛盾属性的类有什么意义,我想我永远不会 知道。
更奇怪的是,只有在Azure WebRole上托管WCF服务时才会出现超时问题。如果你在其他地方托管它,它会抛出一个CommunicationException
,这是有道理的。我猜在Azure那里必须有某种bug。
无论如何,我通过使 User
类不继承自 MembershipUser
并且替换了 MembershipUserCollection
IEnumerable<User>
。
希望这有助于其他人。