-1

I have a simple WCF service hosted in IIS7 using the HTTP protocol. The service contains a method which returns a custom object called Calendar. This object is very basic, and contains simple value-type properties, with the exception of one property, Holidays, which is of type List<IHoliday>. Holiday is again, a simple type comprised of value-type properties only.

The Calendar object is returned from the WCF service-method to an MVC controller, and is applied to a corresponding view. During memory-testing using dotTrace, it's apparent that the Calendar object is finalised by the GC, but interestingly, its Holidays property, which is empty, remains on the heap.

It's not a major performance-issue, taking up a mere 32 bytes, but I'm interested to know why the empty list is not disposed. I can provide code-samples, if necessary.

Source code and service configuration below:

public class Calendar : ICalendar
{
    [DataMember] private IEnumerable<IHoliday> holidays = new List<IHoliday>();
    [DataMember] private IEnumerable<IHolidayNotTaken> holidaysNotTaken = new List<IHolidayNotTaken>();
    [DataMember] private IEnumerable<INonInstructionalDay> nonInstructionalDays = new List<INonInstructionalDay>();
    [DataMember] private IEnumerable<ISchoolBreak> schoolBreaks = new List<ISchoolBreak>();
}

public class Holiday : IHoliday, IIdentifiable, IDisposable
{
    [DataMember(Name = @"date")] [JsonProperty(PropertyName = @"date")] private string date;

    [DataMember(Name = @"checked")]
    [JsonProperty(PropertyName = @"checked")]
    public bool Checked { get; set; }

    /// <summary>
    ///   Gets or sets the end Holiday Id.
    /// </summary>
    /// <value> The holiday id. </value>
    [DataMember(Name = @"id")]
    [JsonProperty(PropertyName = @"id")]
    public int HolidayId { get; set; }

    /// <summary>
    ///   Gets or sets the end description for the holiday.
    /// </summary>
    /// <value> The description. </value>
    [DataMember(Name = @"description")]
    [JsonProperty(PropertyName = @"description")]
    public string Description { get; set; }

    /// <summary>
    ///   Gets or sets the date of the holiday.
    /// </summary>
    /// <value> The holiday date. </value>
    [DataMember]
    [JsonIgnore]
    public DateTime Date { get; set; }

    /// <summary>
    ///   Gets or sets the sort order for the holiday.
    /// </summary>
    /// <value> The sort order. </value>
    [DataMember(Name = @"sortOrder")]
    [JsonProperty(PropertyName = @"sortOrder")]
    public int SortOrder { get; set; }

    /// <summary>
    ///   Gets the <see cref="IIdentifiable.Type" /> of this instance.
    /// </summary>
    [DataMember(Name = @"type")]
    [JsonProperty(PropertyName = @"type")]
    public string Type { get; private set; }

    /// <summary>
    ///   Invoked when this instance is serialising.
    /// </summary>
    /// <param name="streamingContext"> The streaming context. </param>
    [OnSerializing]
    private void OnSerialising(StreamingContext streamingContext)
    {
        date = Date.ToString(@"yyyy-MM-dd");
        Type = GetType().ToString();
    }
}

Service Method:

public Domain.Calendar GetCalendarByMember(string externalId)
    {
        try
        {
            using (var e = new EPlannerEntities())
            {
                var memberId = e.Members
                    .Where(m => m.ExternalId == externalId)
                    .Select(m => m.MemberId)
                    .SingleOrDefault();

                if (memberId.Equals(0))
                {
                    e.Members.AddObject(new Member { ExternalId = externalId.Trim() });

                    var calendar = new Domain.Calendar
                                   {
                                       DefaultViewId = 2,
                                       MemberId = memberId,
                                       IsWeekendsVisible = true,
                                       TimeFormatId = 1,
                                       DayBeginTime = new TimeSpan(0, 8, 0, 0),
                                       DayEndTime = new TimeSpan(0, 16, 0, 0),
                                       DateFormatId = 1
                                   };

                    e.Calendars.AddObject(calendar);
                    e.SaveChanges();

                    return calendar;
                }

                return e.Calendars.Single(c => c.MemberId == memberId);
            }
        }
        catch (Exception exception)
        {
            throw ErrorManager.FaultException(ExceptionType.Business, (int) CalendarErrorCodes.GeneralError, exception);
        }
    }

Service Configuration:

<services>
  <service behaviorConfiguration="CalendarServiceBehavior" name="HMH.ePlanner.Services.Calendar">
    <endpoint address="" binding="netTcpBinding" bindingConfiguration="TCPBinding" name="TCPEndPoint" contract="HMH.ePlanner.Services.ICalendar">
      <identity>
        <dns value="localhost" />
      </identity>
    </endpoint>
    <endpoint address="" binding="basicHttpBinding" bindingConfiguration="HTTPBinding" name="HttpEndPoint" contract="HMH.ePlanner.Services.ICalendar" />
    <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration="" name="TCPMexEndPoint" contract="IMetadataExchange" />
    <host>
      <baseAddresses>
        <add baseAddress="net.tcp://localhost:808/Calendar.svc" />
      </baseAddresses>
    </host>
  </service>
</services>
Paul Mooney
  • 1,576
  • 12
  • 28

3 Answers3

1

The problem stems from the fact that the Holiday list is instantiated without specifying an UpperBound. Specifying an UpperBound ensures that the object is removed from the Heap after garbage-collection.

Paul Mooney
  • 1,576
  • 12
  • 28
  • 1
    Actually this does not make much sense. `List<>` does not have an Upperbound. – H H Jun 14 '12 at 16:48
  • I should have said Capacity instead of Upperbound. Explicitly instantiating the Capacity in the list's constructor ensures that the object does not enter Generation #2. – Paul Mooney Jun 14 '12 at 17:15
0

Make a partial class for Calendar (if partial needed) and add the IDisposable interface to it and manually null out holiday. Run dottrace again and see if the same happens.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
  • Thanks for your reply. I tried this, and also Inherited 'Holiday' from IDisposable, and invoked its 'Dispose' method in 'Calendar.Dispose', which yielded the same result; the object remains on the heap after GC. – Paul Mooney Jun 13 '12 at 16:53
  • I wonder is it due to the fact that the list is declared and up-cast to 'IEnumerable', which is immutable. It may retain a handle to the 'Holiday' objects, regardless of GC. – Paul Mooney Jun 13 '12 at 17:06
0

I do not have an answer for your question because I do not see anything criminal in provided code.

The only recommendation I can made is to check if Session and Security are required in solution.

Now it is not consistent. Configuration enables it for client who uses TCP and disables it for HTTP.

P.S. bindingConfiguration is missed in configuration. Did you post everything?

Dmitry Harnitski
  • 5,838
  • 1
  • 28
  • 43
  • Thank you, as you said, nothing wrong with the code. I'm refactoring the code as another task, which will encompass security, etc. – Paul Mooney Jun 13 '12 at 16:55