automapper could leak to memory leaks

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
Hace 9 años
Nop only uses automapper on Admin, but it stills load right from the start with nop.web project

public class AutoMapperStartupTask : IStartupTask
    {
        public void Execute()
        {
            //TODO remove 'CreatedOnUtc' ignore mappings because now presentation layer models have 'CreatedOn' property and core entities have 'CreatedOnUtc' property (distinct names)
            
            //address
            Mapper.CreateMap<Address, AddressModel>()
                .ForMember(dest => dest.AddressHtml, mo => mo.Ignore())
                .ForMember(dest => dest.AvailableCountries, mo => mo.Ignore())
                .ForMember(dest => dest.AvailableStates, mo => mo.Ignore())
                .ForMember(dest => dest.FirstNameEnabled, mo => mo.Ignore())
                .ForMember(dest => dest.FirstNameRequired, mo => mo.Ignore())
                .ForMember(dest => dest.LastNameEnabled, mo => mo.Ignore())
....


not sure if this class can dispose itself. From my first test with my project after removing the automapper with the same proj with automapper, it seems that nop without automapper loads faster and use less memory (less than almost 40% memory)

how to test: kill both application pool and let the project with automapper load first, it's still slower than the one without automaper (on the same chrome browser)

I will try with a fresh install nop 3.3 later.
Hace 9 años
my domain is temp sub domain, so 0 user other than myself. After letting them alone, both might run some schedule tasks behind the scene, now the automapper pool stabilizes at 600k, and the non-automapper is at 317k memory.
Hace 9 años
Thanks for info. But not sure that I got what exactly you're suggesting. Do not load automapper until admin area pages requested? What exactly object should be disposed?
Hace 9 años
simply remove automapper completely and write the mappingextensions manually just like in Nop.web. forex:

public static CategoryModel ToModel(this Category entity)
        {
            if (entity == null)
                return null;

            var model = new CategoryModel()
            {
                Id = entity.Id,
                Name = entity.Name,
                Description = entity.GetLocalized(x => x.Description),
                MetaKeywords = entity.GetLocalized(x => x.MetaKeywords),
                MetaDescription = entity.GetLocalized(x => x.MetaDescription),
                MetaTitle = entity.GetLocalized(x => x.MetaTitle),
                SeName = entity.GetSeName(),
                //ParentId = entity.ParentCategoryId,
                //IsMultiColumn = entity.IsMultiColumn,
                DisplayOrder = entity.DisplayOrder

            };
            return model;
        }
....
so the system just call the method when it needs, no preloaded or anything, no memory use. That's what i did.
Hace 9 años
Yeah I'm pretty mixed on Automapper in the admin area...a lot of the code seems as "long-winded" as manually doing it. It doesn't work with the EntityFramework proxy objects hence all the ignore member stuff, bit of a pain in the ass!

We've manually wrote our own "mapper" which takes a source object and destination Type, and as long as both implement a marker interface then matching properties/nested objects get mapped from source to a new instance of the destination.

Pretty cool, uses reflection
Hace 9 años
sproaty wrote:
We've manually wrote our own "mapper" which takes a source object and destination Type, and as long as both implement a marker interface then matching properties/nested objects get mapped from source to a new instance of the destination.

Pretty cool, uses reflection

Could you please share this implementation of this "mapper?
Hace 9 años
Yeah I should be able to; unsure on my company's policy of distributing code but it's one method...should be ok. I'm back in work Monday, will try and remember to post :)

The API is pretty clean:

public interface IMappable { } // empty interface

public class DomainObject : IMappable { ... }

public class ViewModel : IMappable { .. }


Controller:

var domainInstance = _someService.getSomething(someParameter)

var viewModel = domainInstance.MapTo<ViewModel>();

return View(viewModel);



We use the interface so that the extension method isn't available on every object which we felt was a bit too extreme.
Hace 9 años
After some investigation (removed automapper, tested in red-gate memory profiler) I've found out that it works just fine and does not effect performance (about 1-2%). Also according to other people investigations (e.g. here) it also should not affect performance when number of process records is less than 1,000 (nopCommerce usually maps about 10-20 records).

Have yo done any measurements in memory profilers? Could you please share your screenshots?
Hace 9 años
Sorry for the delay; here's my Extensions class for our simple object mapper. It's easier to use than AutoMapper, IMO.

I'll start with some examples of its usage:
(assuming all domain & model classes implement the IMappable interface):


// domain -> model
ModelClass model = domainObject.MapTo<ModelClass>();

// model -> domain
ModelClass model = domainObject.MapTo<ModelClass>();

// add extra properties
Address defaultAddress = model.MapTo<Address>(customer.BillingAddress);

// model -> domain; setting extra properties via lambda
ProductReview review = model.MapTo<ProductReview>(r =>
{
    r.CustomerId = _workContext.CurrentCustomer.Id;
    r.IsApproved = false;
    r.CreatedOnUtc = DateTime.Now;
});



source code:



using System;
using Nop.Core.Infrastructure;
using Nop.Web.Framework.Mvc;

namespace Nop.Web.Framework
{
    public interface IMappable
    {
    }

    public static class MappingExtensions
    {
        /// <summary>
        /// Get an object of type 'toType', created from the current object.
        /// </summary>
        /// <typeparam name="toType">The type to map to</typeparam>
        /// <param name="from">The object to map from</param>
        /// <returns>The mapped object</returns>
        public static toType MapTo<toType>(this IMappable from) where toType : IMappable, new()
        {
            var newTo = new toType();
            return (toType)from.MapRecursive(newTo);
        }

        /// <summary>
        /// Get an object of type 'toType' created from the to object and replacings the properties matched in from.
        /// This will also update the 'to' object's properties.
        /// </summary>
        /// <typeparam name="toType">The type to map to</typeparam>
        /// <param name="from">The object to map from</param>
        /// <param name="to">The object to use for mapping</param>
        /// <returns>The mapped object</returns>
        public static toType MapTo<toType>(this IMappable from, IMappable to) where toType : IMappable
        {
            return (toType)from.MapRecursive(to);
        }

        /// <summary>
        /// Get an object of type 'toType' created from the to object and replacings the properties matched in from.
        /// This will also update the 'to' object's properties.
        /// </summary>
        /// <typeparam name="toType">The type to map to</typeparam>
        /// <param name="from">The object to map from</param>
        /// <param name="to">The object to use for mapping</param>
        /// <param name="initalizer">Action to invoke, to set extra properties upon the returned object</param>
        /// <returns>The mapped object</returns>
        public static toType MapTo<toType>(this IMappable from, IMappable to, Action<toType> initalizer) where toType : IMappable
        {
            var newTo = (toType)from.MapRecursive(to);
            initalizer(newTo);
            return newTo;
        }

        /// <summary>
        /// Get an object of type 'toType' created from the current object and set properties on the return object in the action.
        /// </summary>
        /// <typeparam name="toType">The type to map to</typeparam>
        /// <param name="from">The object to map from</param>
        /// <param name="initalizer">Action to invoke, to set extra properties upon the returned object</param>
        /// <returns>The mapped object</returns>
        public static toType MapTo<toType>(this IMappable from, Action<toType> initalizer) where toType : IMappable, new()
        {
            var newTo = new toType();
            newTo = (toType)from.MapRecursive(newTo);
            initalizer(newTo);
            return newTo;
        }

        private static object MapRecursive(this object from, object newTo)
        {
            foreach (var toProp in newTo.GetType().GetProperties())
            {
                foreach (var fromProp in from.GetType().GetProperties())
                {
                    if (fromProp.CanRead && toProp.CanWrite)
                    {
                        try
                        {
                            // Same name and type
                            if (fromProp.Name.ToLower() == toProp.Name.ToLower()
                                && fromProp.PropertyType == toProp.PropertyType)
                            {
                                var fromValue = fromProp.GetValue(from);
                                toProp.SetValue(newTo, fromValue);
                            }
                            // Same name but to property is nullable
                            else if (fromProp.Name.ToLower() == toProp.Name.ToLower()
                                && fromProp.GetValue(from).GetType().Namespace.StartsWith("System")
                                && Nullable.GetUnderlyingType(toProp.PropertyType) != null)
                            {
                                Type underlying = Nullable.GetUnderlyingType(toProp.PropertyType);
                                object fromValue = Convert.ChangeType(fromProp.GetValue(from), underlying);
                                toProp.SetValue(newTo, fromValue);
                            }
                            // Same name but different system types
                            else if (fromProp.Name.ToLower() == toProp.Name.ToLower()
                                && fromProp.GetValue(from).GetType().Namespace.StartsWith("System")
                                && toProp.GetValue(newTo).GetType().Namespace.StartsWith("System"))
                            {
                                object fromValue = Convert.ChangeType(fromProp.GetValue(from), toProp.GetValue(newTo).GetType());
                                toProp.SetValue(newTo, fromValue);
                            }
                            // Same name but different complex types
                            else if (fromProp.Name.ToLower() == toProp.Name.ToLower())
                            {
                                toProp.SetValue(newTo, fromProp.GetValue(from).MapRecursive(toProp.GetValue(newTo)));
                            }
                        }
                        catch { }
                    }

                }
            }
            return newTo;
        }
    }
}
Hace 5 años
Hi

We have a similar issue with a client's setup its nop 3.9, we get memory leaks, that use all the ram on the server, and we have to restart the www service, but we can see that it slowly uses more and more memory...

We did a update of the automapper, but did'nt help.
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.