Note that there are some explanatory texts on larger screens.

plurals
  1. POWCF timeout against Azure WebRole when serializing certain types of collections
    primarykey
    data
    text
    <p>I have a stateless backend service hosted on an <strong>Azure Web Role</strong> which queries an <strong>Entity Framework</strong> container, among other things. Clients communicate with it using <strong>WCF</strong> and <code>wsHttpBinding</code>. <strong>The clients receive a timeout from backend when calling certain methods</strong>, and even though I worked around this in some cases, I can't seem to pinpoint the underlying cause.</p> <p>Here's what I know so far:</p> <ul> <li>The failing methods seem to be the ones that return certain types of collections. This includes <code>IEnumerable&lt;T&gt;</code> with results obtained from LINQ queries, and <code>MembershipUserCollection</code>.</li> <li>In some cases the workaround was to add <code>.ToList()</code> to the collection being returned.</li> <li>Those methods fail <strong>only</strong> when the backend is hosted in Azure. </li> <li>The Azure load balancer doesn't seem to be the cause but the symptom: every time a method times out, the log reveals that it had actually exited long before the 1 minute limit.</li> </ul> <p>And here's one of the methods that fail:</p> <p><strong>WCF interface</strong>:</p> <pre><code> [OperationContract(Action = "http://tempuri.org/IBackendService/GetAllUsers", ReplyAction = "http://tempuri.org/IBackendService/GetAllUsersResponse")] MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords); </code></pre> <p><strong>WCF Implementation</strong> (simplified):</p> <pre><code> 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 =&gt; users.Add(Mappings.Map&lt;UserEntity, User&gt;(ue))); totalRecords = userEntities.Count(); return users; } </code></pre> <p><strong>Mappings</strong> is a class that defines mapping rules between data and business objects using AutoMapper. <code>User</code> inherits from <code>MembershipUser</code>. Here's what's relevant from it: (WIP. Please excuse the mess.)</p> <pre><code> Mapper.CreateMap&lt;UserEntity, User&gt;() .ConstructUsing(ue =&gt; 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&lt;CustomerEntity, Customer&gt;).ToList(), ue.Roles.Select(Map&lt;RoleEntity, Role&gt;).ToList(), ue.Roles.SelectMany(r =&gt; r.Activities).Select(Map&lt;ActivityEntity, Activity&gt;).ToList() )) .IgnoreAllNonExisting(); Mapper.CreateMap&lt;User, UserEntity&gt;() .ForMember(ue =&gt; ue.Comment, opt =&gt; opt.MapFrom(u =&gt; u.Comment)) .ForMember(ue =&gt; ue.CreationDate, opt =&gt; opt.MapFrom(u =&gt; u.CreationDate)) .ForMember(ue =&gt; ue.Email, opt =&gt; opt.MapFrom(u =&gt; u.Email)) .ForMember(ue =&gt; ue.IsApproved, opt =&gt; opt.MapFrom(u =&gt; u.IsApproved)) .ForMember(ue =&gt; ue.IsLockedOut, opt =&gt; opt.MapFrom(u =&gt; u.IsLockedOut)) .ForMember(ue =&gt; ue.LastActivityDate, opt =&gt; opt.MapFrom(u =&gt; u.LastActivityDate.Equals(DateTime.MinValue) ? (DateTime?)null : u.LastActivityDate)) .ForMember(ue =&gt; ue.LastLockoutDate, opt =&gt; opt.MapFrom(u =&gt; u.LastLockoutDate.Equals(DateTime.MinValue) ? (DateTime?)null : u.LastLockoutDate)) .ForMember(ue =&gt; ue.LastLoginDate, opt =&gt; opt.MapFrom(u =&gt; u.LastLoginDate.Equals(DateTime.MinValue) ? (DateTime?)null : u.LastLoginDate)) .ForMember(ue =&gt; ue.LastPasswordChangedDate, opt =&gt; opt.MapFrom(u =&gt; u.LastPasswordChangedDate.Equals(DateTime.MinValue) ? (DateTime?)null : u.LastPasswordChangedDate)) .ForMember(ue =&gt; ue.PasswordQuestion, opt =&gt; opt.MapFrom(u =&gt; u.PasswordQuestion)) .ForMember(ue =&gt; ue.ProviderName, opt =&gt; opt.MapFrom(u =&gt; u.ProviderName)) .ForMember(ue =&gt; ue.UserId, opt =&gt; opt.MapFrom(u =&gt; (int)u.ProviderUserKey)) .ForMember(ue =&gt; ue.UserName, opt =&gt; opt.MapFrom(u =&gt; u.UserName)) .ForMember(ue =&gt; ue.Password, opt =&gt; opt.Ignore()) .ForMember(ue =&gt; ue.PasswordAnswer, opt =&gt; opt.Ignore()) .ForMember(ue =&gt; ue.ApplicationName, opt =&gt; opt.Ignore()) .ForMember(ue =&gt; ue.Roles, opt =&gt; opt.Ignore()) .ForMember(ue =&gt; ue.Customers, opt =&gt; opt.Ignore()) .IgnoreAllNonExisting(); </code></pre> <p><strong>IgnoreAllNonExisting</strong> is an extension method:</p> <pre><code> public static IMappingExpression&lt;TSource, TDestination&gt; IgnoreAllNonExisting&lt;TSource, TDestination&gt;(this IMappingExpression&lt;TSource, TDestination&gt; expression) { var sourceType = typeof(TSource); var destinationType = typeof(TDestination); var existingMaps = Mapper.GetAllTypeMaps().First(x =&gt; x.SourceType == sourceType &amp;&amp; x.DestinationType == destinationType); foreach (var property in existingMaps.GetUnmappedPropertyNames()) { expression.ForMember(property, opt =&gt; opt.Ignore()); } return expression; } </code></pre> <p>My best guess is that this is a serialization problem in the WCF layer, but I don't know what might be triggering it. Other than that, I'm completely clueless. </p> <p>What else should I check? Do you need any more info?</p> <p><strong>EDIT: Added moar snippets for clarification.</strong></p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload