是否可以简化泛型类?

发布于 2024-12-11 05:40:51 字数 2685 浏览 3 评论 0原文

同事, 在我们的项目中,我们使用 AutoMapper 来映射模型。

我们有一个模型:

public class Location
{
   public string Address { get; set; }
}

public class Person
{
   public string Name { get; set;}
   public Collection<Location> Locations { get; set; }
}

我们也有一个视图模型:

public class PersonView
{
   public string Name { get; set; }
   public string Location { get; set;}
}

为了将模型映射到视图模型,我们可以定义如下内容:

Mapper.CreateMap<Person, PersonView>(d=>d.Name, opt=>opt.FromMap(s=>s.Name);
Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.FromMap(s=>s.Locations.First().Address);

但是:如果 Locations 不包含元素或者为 null,那么我们将得到一个异常。

从另一方面来说,我们可以定义一个函数来获取一个值:

Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.FromMap(s=>
{
    var item = s.Locations.FirstOrDefault();
    if(item == null)
    {
       return string.Empty;
    }

    return item.Address;
});

这个表达式很难读。我尝试创建一个 IValueResolver 来简化映射。

public class CollectionItemResolver<TSource, TSourceItem, TResult>
    where TSource : class
    where TSourceItem : class
{
    private readonly Func<TSource, IEnumerable<TSourceItem>> _sourceSelector;
    private readonly Func<TSourceItem, TResult> _selector;
    private readonly TResult _defaultValue;

    public CollectionItemResolver(Func<TSource, IEnumerable<TSourceItem>> source, Func<TSourceItem, TResult> selector)
        : this(source, selector, default(TResult))
    {
    }

    public CollectionItemResolver(Func<TSource, IEnumerable<TSourceItem>> source, Func<TSourceItem, TResult> selector, TResult defaultValue)
    {
        _sourceSelector = source;
        _selector = selector;
        _defaultValue = defaultValue;
    }

    public TResult Resolve(TSource source)
    {
        var items = _sourceSelector(source);

        if (items == null)
        {
            return _defaultValue;
        }

        var item = items.FirstOrDefault();
        if (item == null)
        {
            return _defaultValue;
        }

        var value = _selector(item);
        return value;
    }
}

然后使用这样的东西:

Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.ResolveUsing(
    new CollectionItemResolver<Person, Location, string>(p=>p.Locations, i=>i.Address)));

Is possible simple generic rever? 例如不定义嵌套项的类型?

new CollectionItemResolver<Person, string>(p=>p.Locations, i=>i.Address)));

谢谢,

Colleagues,
In our project we are using AutoMapper to map models.

We have a model:

public class Location
{
   public string Address { get; set; }
}

public class Person
{
   public string Name { get; set;}
   public Collection<Location> Locations { get; set; }
}

also we have a view model:

public class PersonView
{
   public string Name { get; set; }
   public string Location { get; set;}
}

To mapping a model to a view model we may define something like the following:

Mapper.CreateMap<Person, PersonView>(d=>d.Name, opt=>opt.FromMap(s=>s.Name);
Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.FromMap(s=>s.Locations.First().Address);

BUT: If Locations will not contains elements or is null then we will get an exception.

From other side, we may define a function to get a value:

Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.FromMap(s=>
{
    var item = s.Locations.FirstOrDefault();
    if(item == null)
    {
       return string.Empty;
    }

    return item.Address;
});

This expressions hard to read. And I try create a IValueResolver for simplify mapping.

public class CollectionItemResolver<TSource, TSourceItem, TResult>
    where TSource : class
    where TSourceItem : class
{
    private readonly Func<TSource, IEnumerable<TSourceItem>> _sourceSelector;
    private readonly Func<TSourceItem, TResult> _selector;
    private readonly TResult _defaultValue;

    public CollectionItemResolver(Func<TSource, IEnumerable<TSourceItem>> source, Func<TSourceItem, TResult> selector)
        : this(source, selector, default(TResult))
    {
    }

    public CollectionItemResolver(Func<TSource, IEnumerable<TSourceItem>> source, Func<TSourceItem, TResult> selector, TResult defaultValue)
    {
        _sourceSelector = source;
        _selector = selector;
        _defaultValue = defaultValue;
    }

    public TResult Resolve(TSource source)
    {
        var items = _sourceSelector(source);

        if (items == null)
        {
            return _defaultValue;
        }

        var item = items.FirstOrDefault();
        if (item == null)
        {
            return _defaultValue;
        }

        var value = _selector(item);
        return value;
    }
}

And then use something like this:

Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.ResolveUsing(
    new CollectionItemResolver<Person, Location, string>(p=>p.Locations, i=>i.Address)));

Is possible simplify generic resolver?
For instance do not define the type of nested item?

new CollectionItemResolver<Person, string>(p=>p.Locations, i=>i.Address)));

Thanks,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

优雅的叶子 2024-12-18 05:40:51

这个怎么样:

Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.FromMap(s=>s.Locations.Select(loc=>loc.Address).FirstOrDefault());

买个方式,Automapper知道如何将Null转换为string.Empty

PS,希望你有收藏Locations< /code> 始终不为空。
但如果没有,那么我建议使用这个 扩展

public static IEnumerable<TSource> NullToEmpty<TSource>(
    this IEnumerable<TSource> source)
{
    if (source == null)
        return Enumerable.Empty<TSource>();

    return source;
}

那么结果将是这样的:
opt=>opt.FromMap(s=>s.Locations.NullToEmpty().Select(loc=>loc.Address).FirstOrDefault());

How about this:

Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.FromMap(s=>s.Locations.Select(loc=>loc.Address).FirstOrDefault());

Buy the way, Automapper knows how to convert Null to string.Empty

PS, hope you have collection Locations always not null.
But if not, then I suggest use this extension:

public static IEnumerable<TSource> NullToEmpty<TSource>(
    this IEnumerable<TSource> source)
{
    if (source == null)
        return Enumerable.Empty<TSource>();

    return source;
}

Then result will be something like this:
opt=>opt.FromMap(s=>s.Locations.NullToEmpty().Select(loc=>loc.Address).FirstOrDefault());

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文