Linq to Sql - 如何使用存储库模式更新对象?

时间:2011-01-30 00:04:27

标签: database linq-to-sql repository optimistic-concurrency

有大量关于此的信息,但即使在阅读了几个小时和几个小时后,我似乎无法按照我想要的方式工作。

我正在尝试通过传入User对象并通常将更改与我从数据库中拉出的User对象进行比较来更新User对象。使用此方法时,我总是得到NotSupportedException:

  

已尝试附加或   或许,添加一个非新的实体   从另一个装载   DataContext的。这不受支持。

以下是我尝试这样做的方法:

    public void SaveUser(User User)
    {
        using (DataContext dataContext = new DataContext(WebConfigurationManager.ConnectionStrings["database"].ConnectionString))
        {
            // New user
            if (User.UserID == 0)
            {
                dataContext.Users.InsertOnSubmit(User);
            }
            // Existing user
            else
            {
                User dbUser = dataContext.Users.Single(u => u.UserID.Equals(User.UserID));
                Type t = dbUser.GetType();
                foreach (PropertyInfo p in t.GetProperties())
                {
                    if (p.CanWrite & p.GetValue(dbUser, null) != p.GetValue(User, null))
                    {
                        p.SetValue(dbUser, p.GetValue(User, null), null);
                    }
                }
                //dataContext.Refresh(RefreshMode.KeepCurrentValues, dbUser);
            }
            dataContext.SubmitChanges();
        }
    }

我评论过的评论栏也没有注释,但没有任何帮助。

如果我注释掉foreach()循环并添加一行像dbUser.UserName =“Cheese”;它会更新数据库中的用户名。这让我相信它与foreach()循环如何更改导致此失败的dbUser对象有关。

当我调试dbUser对象时,它似乎正确地获取了作为参数传递的User对象的所有更改。

我还对乐观并发做了一些阅读,并在数据类型时间戳表中添加了一列,但这似乎也没有任何效果。

我到底错在了什么?

如何通过一般性地检测已更改的内容并将更改正确保存到数据库?

2 个答案:

答案 0 :(得分:2)

我的猜测是你正在尝试复制的外键关系(最初没有加载)(因为延迟加载)在复制期间,它正在尝试加载它,但DataContext已经被处理掉了。

我一直在研究类似的问题。我最终使用AutoMapper处理复制属性。我已将AutoMapper配置为忽略主键字段以及任何关系。类似的东西:

public void Update(User user)
{
     using (var db = new DataContext(...))
     {
         var userFromDb = db.Users.Where(x => x.Id == user.Id).Single();
         AutoMapper.Mapper.Map(user, userFromDb);
         db.SubmitChanges();
    }
}

我的自动播放器配置类似于

AutoMapper.Mapper.Create<User, User>().ForMember(dest => dest.Id, opt => opt.Ignore())
                                      .ForMember(dest => dest.SomeRelation, opt => opt.Ignore());

您可以在此处找到AutoMapper:http://automapper.codeplex.com/

答案 1 :(得分:0)

我让我的回购非常精益,唯一的工作就是与数据库进行交互。我在repo之上构建了一个Service层,它可以做更多的工作

public class EventRepository : IEventRepository
{

    private DBDataContext dc;
    public EventRepository()
    {
        dc = new DBDataContext();
    }

    public void Create(Event @event)
    {
        dc.Events.InsertOnSubmit(@event);
    }

    public System.Linq.IQueryable<Event> Read()
    {
        object events = (from e in dc.Eventse);
        return events.AsQueryable;
    }

    public void SubmitChanges()
    {
        dc.SubmitChanges();
    }

}

然后来自服务层的相应调用看起来像这个

public void AddEvent(Event @event)
{
    _EventRepository.Create(@event);
}

public void SubmitChanges()
{
    _EventRepository.SubmitChanges();
}

我从我的控制器那里打电话。

// AutoMapper will allow us to map the ViewModel with the DomainModel
Mapper.CreateMap<Domain.ViewModels.EventsAddViewModel, Domain.Event>();
object @event = Mapper.Map<Domain.ViewModels.EventsAddViewModel, Domain.Event>(eventToAdd);

// Add the event to the database
EventService.AddEvent(@event);
EventService.SubmitChanges();