Search code examples
c#.netlinq-to-sqlgarbage-collection

System.Data.Linq.Mapping.AttributedMetaDataMember High memory consumption


I'm analyzing the dump file for the Windows service project, and it is using LinqToSQL to connect databases. Attached dumpe heap memory analysis summary.

I can see the System.Data.Linq.Mapping.AttributedMetaDataMember taking more memory and It's resides in Gen2 bucket.

Can anyone guide me why it's taking more memory here?

Sample Code

public class Program
{
    static void Main(string[] args)
    {
        var detailsDataContext = new DetailsContext();
        var details = detailsDataContext.GetDetails(//param);
    }
}

public class DetailsContext
{
    private readonly IDetailsDataContextRepository _detailsDataContextRepository;

    public DetailsContext()
    {
        _detailsDataContextRepository = new DetailsDataContextRepository();
    }

    public Details GetDetails(string id)
    {
        try
        {
            List<Details> detailsList = _detailsDataContextRepository.GetDetails(id)?.ToList();
            if (detailsList != null && detailsList.Count > 0)
            {
                Detail detail = detailsList.FirstOrDefault();
                return detailsList;
            }

            return null;

        }
        catch (Exception ex)
        {
            //Error log
            return null;
        }
    }

}

public class DetailsDataContextRepository : DataContext, IDetailsDataContextRepository
{

    [Function(Name = "dbo.sp_GetDetails")]
    public IEnumerable<Details> GetDetails([Parameter(Name = "Id", DbType = "VARCHAR(255)")] string id)
    {
        try
        {
            IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), id);
            ISingleResult<Details> mRecord = ((ISingleResult<Details>)(result.ReturnValue));
            return mRecord;
        }
        catch (Exception ex)
        {
            TraceError("DetailsDataContext", "GetDetails", ex);
            return new List<Details>();
        }
    }
}

Thanks in Advance. enter image description here


Solution

  • I'll focus on DetailsContext.GetDetails(string id) method as one example of how to improve things.

    Instead of this:

    public Details GetDetails(string id)
    {
        try
        {
            List<Details> detailsList = _detailsDataContextRepository.GetDetails(id)?.ToList();
            if (detailsList != null && detailsList.Count > 0)
            {
                Detail detail = detailsList.FirstOrDefault();
                return detailsList;
            }
    
            return null;
    
        }
        catch (Exception ex)
        {
            //Error log
            return null;
        }
    }
    

    You can get equivalent functionality like this:

    public Details GetDetails(string id)
    {
        try
        {
            return _detailsDataContextRepository.GetDetails(id).FirstOrDefault();
        }
        catch (Exception ex)
        {
            //Error log
            return null;
        }
    }
    

    Not only is this MUCH less code, but it's faster and far more memory efficient. As mentioned in the comment, one of the main improvements here is eliminating the spurious ToList() call, which accomplished nothing useful. I see a lot of programmers adopt this crutch, and learning to work without it can dramatically help your code.

    Furthermore, based on a review of the repository's GetDetails() method, I might also remove the try/catch block, since that method itself uses a try/catch around anything that's likely to throw and provides a sensible default. It means we can safely get down to this one-liner with no loss of function:

    public Details GetDetails(string id)
    {
        return _detailsDataContextRepository.GetDetails(id).FirstOrDefault();
    }