自动映射:手动映射属性

时间:2015-05-08 11:46:41

标签: c# automapper

我刚刚开始使用automapper来映射DTO< - >实体,它看起来效果很好。

在某些特殊情况下,我只想映射一些属性并执行其他检查。没有automapper,代码看起来像这样(使用fasterflect的PropertyExtensions):

object target;
object source;
string[] changedPropertyNames = { };

foreach (var changedPropertyName in changedPropertyNames)
{
    var newValue = source.GetPropertyValue(changedPropertyName);
    target.SetPropertyValue(changedPropertyName, newValue);
}

当然,如果需要进行类型转换,此代码将无效。 Automapper使用内置的TypeConverters,我还创建了一些特定的TypeConverter实现。

现在我想知道是否可以映射单个属性并使用automapper的类型转换实现,类似这样的

Mapper.Map(source, target, changedPropertyName);

更新

我认为有必要提供更多信息:

我已经创建了一些地图,例如

Mapper.CreateMap<CalendarEvent, CalendarEventForm>()

我还在CalendarEvent中为可为空的dateTime属性创建了一个带有自定义typeconverter的地图,例如。

Mapper.CreateMap<DateTimeOffset?, DateTime?>().ConvertUsing<NullableDateTimeOffsetConverter>();

我在web api OData Controller中使用这些地图。发布新的EntityDTO时,我使用

Mapper.Map(entityDto, entity);

并将实体保存到数据存储区。

但是如果使用PATCH,则会将Delta<TDto> entityDto传递给我的控制器方法。因此,我需要调用entityDto.GetChangedPropertyNames()并使用更改的值更新现有的持久实体。

基本上这适用于我的简单解决方案,但如果其中一个已更改的属性是一个DateTimeOffset?我想使用我的NullableDateTimeOffsetConverter

3 个答案:

答案 0 :(得分:18)

如果您只想映射一些选择属性,而不是如下所示:

// Create a map
var map = CreateMap<Source,Target>();
// ingnore all existing binding of property
map.ForAllMembers(opt => opt.Ignore());
// than map property as following
map.ForMember(dest => dest.prop1, opt => opt.MapFrom( src => src.prop1));
map.ForMember(dest => dest.prop2, opt => opt.MapFrom( src => src.prop2));

答案 1 :(得分:8)

您可以使用 MapFrom 方法进行投影 - http://automapper.readthedocs.io/en/latest/Projection.html

Mapper.Map(source, target)
   .ForMember(m => m.Property, opt => opt.MapFrom(src => src.ChangedProperty));

例如(关注AutoMapper文档):

// Model
var calendarEvent = new CalendarEvent
    {
        Date = new DateTime(2008, 12, 15, 20, 30, 0),
        Title = "Company Holiday Party"
    };

// Configure AutoMapper
Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
    .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.Date.Date))
    .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.Date.Hour))
    .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.Date.Minute));

答案 2 :(得分:1)

如果我正确地阅读了您的问题,是的,只要您的目的地(目标)属性与您的转化匹配。

因此,如果我从 override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. Randomize() Hide() authenticateLocalPlayer() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func Submit(sender: AnyObject) { saveHighscore(Score) showLeaderboard() } func authenticateLocalPlayer(){ var localPlayer = GKLocalPlayer.localPlayer() localPlayer.authenticateHandler = {(viewController, error) -> Void in if (viewController != nil) { self.presentViewController(viewController, animated: true, completion: nil) } else { println((GKLocalPlayer.localPlayer().authenticated)) } } } func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController!) { gameCenterViewController.dismissViewControllerAnimated(true, completion: nil) } func showLeaderboard() { var vc = self.view?.window?.rootViewController var gc = GKGameCenterViewController() gc.gameCenterDelegate = self vc?.presentViewController(gc, animated: true, completion: nil) } func saveHighscore(score:Int) { //check if user is signed in if GKLocalPlayer.localPlayer().authenticated { var scoreReporter = GKScore(leaderboardIdentifier: "ChineseWeather") //leaderboard id here scoreReporter.value = Int64(Score) //score variable here (same as above) var scoreArray: [GKScore] = [scoreReporter] GKScore.reportScores(scoreArray, withCompletionHandler: {(error : NSError!) -> Void in if error != nil { println("error") } }) } } 转到string bool Status"A"(有效/无效),我可以做类似的东西:

"I"

然后当转向另一个方向时,将其转换回来:

.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status == "A"))

日期示例:

.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status ? "A" : "I"))