我有一个主接口,其他接口继承自该接口。我希望主自动映射器映射根据对象类型切换并返回另一个相关的定义映射。
例如调用主界面IGroup:
public interface IGroup {...}
然后再制作一些继承自该接口的接口:
public interface IFamily : IGroup {...}
public interface IFriends : IGroup {...}
public interface INeighbours : IGroup {...}
我为每个继承的组定义相关映射:
CreateMap<IFamily, GroupListView>() ...etc
在主 IGroup 映射上,我想进行切换,以便如果我有 IGroup 集合,它会返回每个项目的相关映射。
我已经尝试过了
CreateMap<IGroup, GroupListView>()
.Include<IFamily, GroupListView>()
.Include<IFriends, GroupListView>()
.Include<INeighbours, GroupListView>()
.ConstructUsing((IGroup group, ResolutionContext context) =>
{
switch (group)
{
case IFamily family:
return context.Mapper.Map<IFamily, GroupListView>(family);
case IFriends friends:
return context.Mapper.Map<IFriends, GroupListView>(friends);
case INeighbours neighbors:
return context.Mapper.Map<INeighbours, GroupListView>(neighbors);
default:
throw new InvalidOperationException("Unknown group type: " + group.GetType().FullName);
}
});
但是验证抱怨 IGroup 接口上有未映射的成员,因为我实际上没有定义任何内容。
如果我在 opt.Ignore 或 opt.Ignore 之后定义任何内容,那么它将覆盖 ConstructUsing 结果。
如果不是在无法识别的 IGroup 类型上抛出错误,而是返回 IGroup 的默认映射,那就更好了。
非常感谢。
编辑:
有趣。所以看起来有几个问题正在发生。我会将它们记录下来,以防有人遇到类似的问题。 主地图上的开关正在工作,您可以手动定义默认情况(尽管以某种方式为此制作地图会很好)。代码如下所示:
CreateMap<IGroup, GroupListView>()
.Include<IFamily, GroupListView>()
.Include<IFriends, GroupListView>()
.Include<INeighbours, GroupListView>()
.ConstructUsing((IGroup group, ResolutionContext context) =>
{
switch (group)
{
case IFamily family:
return context.Mapper.Map<IFamily, GroupListView>(family);
case IFriends friends:
return context.Mapper.Map<IFriends, GroupListView>(friends);
case INeighbours neighbors:
return context.Mapper.Map<INeighbours, GroupListView>(neighbors);
default:
return new GroupListView() {...} //Would prefer a default map
}
})
.ForAllOtherMembers(opt => opt.Ignore());
有趣的是,尽管有这些接口的映射并在 switch 情况下专门调用它们,但它似乎更喜欢具体的类实现。
我有一个主要的抽象类基:
public abstract class Group {...}
一个接受继承自它的泛型和主接口
public abstract class Group<TPerson, TPlace> : Group, IGroup {...}
然后为每个组类型进行摘要:
public abstract class FamilyGroup<TPerson, TPlace> : Group<TPerson, TPlace> {...}
public abstract class FriendGroup<TPerson, TPlace> : Group<TPerson, TPlace> {...}
public abstract class NeighborGroup<TPerson, TPlace> : Group<TPerson, TPlace> {...}
最后是继承这些抽象类及其接口的具体类
public class BestFriendGroup : FriendGroup<Bestie, Home>, IFriends {...}
现在,由于某种原因,即使在其中一个接口上显式调用映射时,它也更喜欢抽象类。这很奇怪,因为它甚至没有寻找具体的类型。 因此,尽管我的方法返回包含 IFriends 等的 IGroup 集合,但手动返回
context.Mapper.Map<IFriends,GroupListView>(group)
实际上是在获取根抽象类的映射
CreateMap<Group, GroupListView>()...
我不知道为什么。这些地图都不包含彼此作为基础。我什至通过将随机属性映射到组地图上手动定义的字符串来确认这一点,并且正在拾取该地图。
更新2:
好了,这又回到了原点,并且学到了新的东西。事实证明,虽然我的类确实是接口的实例,但自动映射器实际上关注继承的列表顺序,并且将始终选择第一个选项。 详细信息在这里:https://github.com/AutoMapper/AutoMapper/issues/1295
所以我的最终类继承自抽象类和接口,即
public class BestFriendGroup : FriendGroup<Bestie, Home>, IFriends {...}
始终会选择抽象类映射,并且无法更改它,因为类必须始终位于接口之前。
来自 automapper 开发人员:“您在这里遇到的是 AutoMapper 在确定如何映射时会考虑对象的运行时类型。通用参数不会“选择”映射,它们只是在映射下进行一些转换为方便起见,运行时类型胜过一切。”
我将在解决方案下列出我的最终解决方案。
我说我想定义默认地图而不是手动的说法现在很明显,因为整个事情都必须手动定义,这很讽刺。我最终切换到合适类型的解决方案如下:
CreateMap<IGroup, GroupListView>()
.ConstructUsing((group, context) =>
{
var groupView = new GroupListView()
{...shared properties};
switch (group)
{
case IFamily family:
{
groupView.familyPropertyOne = family.foo;
break;
}
case IFriends friends:
{
groupView.neighborPropertyOne = friends.foo;
break;
}
case INeighbours neighbors:
{
groupView.neighborPropertyOne = neighbors.foo;
break;
}
default:
break;
}
return groupView;
})
.ForAllOtherMembers(opt => opt.Ignore());
请注意缺少 .include,因此没有任何内容可以覆盖它。因为它总是从 ConstructUsing 方法返回一些内容,所以我们可以告诉 automapper 忽略其他属性。 您还可以使用流畅的语法定义初始共享属性。完全取决于喜好。