Efficient way of mapping data from Redis

早过忘川 提交于 2019-12-24 17:26:09

问题


I'm playing around with Redis and with ServiceStack.Redis as a client. I initially used 'AutoMapper' to map the cached objects into domain objects, but this was pretty slow. Using someone else's example, I set up a custom mapper but this, too, is really slow.

Is there something glaringly wrong with the below code? It's taking 4-5 seconds to map 1000 items from Redis.

It's the 'GetByIds' client method that's introducing the lag, but I want an efficient way to store collections as lists of IDs in Redis don't see another way to convert these to lists of domain objects.

Thanks!

interface IMapToNew<TSource, TTarget>
{
    TTarget Map(TSource source);
}

interface IMapToExisting<TSource, TTarget>
{
    void Map(TSource source, TTarget target);
}

class FullEmployeeMapper : IMapToNew<Employee, FullEmployee>
{
    public FullEmployee Map(Employee source)
    {
        FullEmployee employee = new FullEmployee()
        {
            Id = source.Id,
            Age = source.Age,
            BirthDate = source.BirthDate,
            Name = source.Name
        };

        var mapper = new FullRoleMapper();
        var client = new RedisClient("localhost");

        employee.Roles =
            client
                .As<Role>()
                .GetByIds(source.Roles)
                .Select(r => mapper.Map(r))
                .ToList();

        return employee;
    }
}

class FullRoleMapper : IMapToNew<Role, FullRole>
{
    public FullRole Map(Role source)
    {
        FullRole role = new FullRole()
        {
            Id = source.Id,
            RoleName = source.RoleName
        };

        return role;
    }
}

class FullEmployee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? Age { get; set; }
    public DateTime? BirthDate { get; set; }
    public IList<FullRole> Roles { get; set; }
}

class FullRole
{
    public int Id { get; set; }
    public string RoleName { get; set; }
}

class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int? Age { get; set; }
    public DateTime? BirthDate { get; set; }
    public IList<int> Roles { get; set; }

    public Employee(int EmployeeId, string Name)
    {
        this.Id = EmployeeId;
        this.Name = Name;
    }
}

class Role
{
    public int Id { get; set; }
    public string RoleName { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var client = new RedisClient("localhost");
        var employeeClient = client.As<Employee>();

        var allEmployees = employeeClient.GetAll();

        var allFullEmployees = 
            allEmployees
                .Select(e => mapper.Map(e))
                .ToList();
    }
}

回答1:


You can use lazy loading so Roles collection is loaded only if needed. This is done by injecting a role repository in your FullEmployee entity.

You could also load the roles once for all : keep a Dictionary of roles in your FullEmployeeMapper and fill it as they are loaded then check it before querying the cache. Hopefully you recreate an instance for each unit of work so the dictionary will be fresh for each new work and you avoid multithreading issues.

Sample implementation with a List :

class FullEmployeeMapper : IMapToNew<Employee, FullEmployee>
{
    private List<FullRole> _roles = new List<FullRole>();
    public FullEmployee Map(Employee source)
    {
        FullEmployee employee = new FullEmployee()
        {
            Id = source.Id,
            Age = source.Age,
            BirthDate = source.BirthDate,
            Name = source.Name
        };

        var mapper = new FullRoleMapper();
        var client = new RedisClient("localhost");

        employee.Roles = _roles.Where(r => source.Roles.Contains(r.Id)).ToList();
        if (employee.Roles.Count != source.Roles.Count)
        {
            var newRoles = client
                .As<Role>()
                .GetByIds(source.Roles.Except(employee.Roles.Select(r => r.Id)))
                .Select(r => mapper.Map(r)))
                .ToList();
            employee.Roles.AddRange(newRoles);
            _roles.AddRange(newRoles);
        }
        return employee;
    }
}



回答2:


Automapper uses reflection which may be slow.

Check out EmitMapper for performance.



来源:https://stackoverflow.com/questions/25814496/efficient-way-of-mapping-data-from-redis

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!