使用外部对象更新文档

Update Document with external object

我有一个包含歌曲对象的数据库。歌曲 class 有 > 30 个属性。

我的音乐标记应用程序正在对文件系统中的歌曲进行更改。 然后它使用文件名在数据库中进行查找。

现在我有一个 Song 对象,它是我通过读取物理文件在我的标记应用程序中创建的,并且我有一个 Song 对象,我刚从数据库中检索到它并且我想更新它。 我以为我可以从数据库对象中获取 ID,用我的本地歌曲对象替换数据库对象,设置保存的 ID 并存储它。 但是 Raven 声称我正在用不同的对象替换该对象。

我真的需要像这样把每个 属性 都复制过来吗?

dbSong.Artist = songfromFilesystem.Artist;

dbSong.Album = songfromFileSystem.Album;

或者还有其他的可能性。

谢谢,

赫尔穆特

编辑:

我有点太积极了。以下建议仅适用于测试程序。 在我的原始代码中执行此操作时,出现以下异常:

Attempted to associate a different object with id 'TrackDatas/3452'

这是由以下代码生成的:

try
  {
    originalFileName = Util.EscapeDatabaseQuery(originalFileName);
    // Lookup the track in the database
    var dbTracks = _session.Advanced.DocumentQuery<TrackData, DefaultSearchIndex>().WhereEquals("Query", originalFileName).ToList();
    if (dbTracks.Count > 0)
    {
      track.Id = dbTracks[0].Id;
      _session.Store(track);
      _session.SaveChanges();
    }
  }
  catch (Exception ex)
  {
    log.Error("UpdateTrack: Error updating track in database {0}: {1}", ex.Message, ex.InnerException);
  }

我首先在数据库中查找一首歌曲并在 dbTracks 中获取一个 TrackData 对象。

轨道对象也是 TrackData 类型,我只是将刚检索到的对象的 ID 放入并尝试存储它,这会出现上述错误。

我认为上面的消息告诉我对象属于不同的类型,但事实并非如此。

如果我使用 AutoMapper,也会发生同样的错误。

有什么想法吗?

您可以做您正在尝试的事情:仅使用 ID 替换现有对象。如果它不起作用,您可能做错了其他事情。 (在这种情况下,请向我们展示您的代码。)

在更新 Raven 中的现有对象时,有几个选项:

选项 1:仅使用与现有对象相同的 ID 保存对象:

var song = ... // load it from the file system or whatever
song.Id = "Songs/5"; // Set it to an existing song ID
DbSession.Store(song); // Overwrites the existing song

选项 2:手动更新现有对象的属性。

var song = ...;
var existingSong = DbSession.Load<Song>("Songs/5");
existingSong.Artist = song.Artist;
existingSong.Album = song.Album;

选项 3:动态更新现有对象:

var song = ...;
var existingSong = DbSession.Load<Song>("Songs/5");
existingSong.CopyFrom(song);

哪里有这样的代码:

// Inside Song.cs
public virtual void CopyFrom(Song other)
{
    var props = typeof(Song)
        .GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
        .Where(p => p.CanWrite);
    foreach (var prop in props)
    {
        var source = prop.GetValue(other);
        prop.SetValue(this, source);
    }
}

如果您发现自己不得不经常这样做,请使用像 AutoMapper.

这样的库

Automapper 只需一行代码即可自动将一个对象复制到另一个对象。

现在您已经发布了一些代码,我看到了两件事:

首先,您使用 Advanced.DocumentQuery 语法有什么原因吗?

// This is advanced query syntax. Is there a reason you're using it?
var dbTracks = _session.Advanced.DocumentQuery<TrackData, DefaultSearchIndex>().WhereEquals("Query", originalFileName).ToList();

以下是我使用标准 LINQ 语法编写代码的方式:

var escapedFileName = Util.EscapeDatabaseQuery(originalFileName);
// Find the ID of the existing track in the database.
var existingTrackId = _session.Query<TrackData, DefaultSearchIndex>()
   .Where(t => t.Query == escapedFileName)
   .Select(t => t.Id);
if (existingTrackId != null)
{
   track.Id = existingTrackId;
   _session.Store(track);
   _session.SaveChanges();
}

最后,#2:什么是track?它是通过 session.Load 还是 session.Query 加载的?如果是这样,那是行不通的,它会导致你的问题。如果曲目是从数据库加载的,您需要创建一个新对象并保存:

 var escapedFileName = Util.EscapeDatabaseQuery(originalFileName);
// Find the ID of the existing track in the database.
var existingTrackId = _session.Query<TrackData, DefaultSearchIndex>()
   .Where(t => t.Query == escapedFileName)
   .Select(t => t.Id);
if (existingTrackId != null)
{
   var newTrack = new Track(...);
   newTrack.Id = existingTrackId;
   _session.Store(newTrack);
   _session.SaveChanges();
}

这意味着您在会话中已经有一个具有相同 ID 的不同对象。我的解决方法是使用新会话。