数据库结构中的Supersedes子句

时间:2012-02-17 22:05:58

标签: nhibernate database-design linq-to-nhibernate

想象一下如下所示的数据库表:

create table [dbo].[user]
(
    id int IDENTITY(1,1),
    username varchar(50) NOT NULL,
    firstname varchar(20) NOT NULL,
    lastname varchar(30) NOT NULL,
    currentid int NULL,
    processedby varchar(50) NOT NULL,
    processeddate varchar(50) NOT NULL
    processedaction varchar(50) NOT NULL
)

我想要做的是设置NHibernate将其加载到我的用户对象中,但我只希望将当前版本的对象“user”带回来。我知道如何自己做一个SQL select来做这个,我觉得nHibernate中有一些东西与触发器和事件监听器的使用,但是任何人都可以告诉我如何实现nHibernate存储库,所以我可以:

  • {Repository} .GetCurrent(id)< - 传递给任何历史记录或当前记录的任何ID,并获取当前对象。
  • {Repository} .Save(user)< - 我希望始终将更改插入新行,然后更新旧版本以链接回新ID。

修改

所以,这里有一些混乱,也许我解释错了......我想要做的就是这样,总是得到当前记录......

Select uc.*
FROM User uo
JOIN User uc on uo.currentid=uc.id
WHERE uo.id==:id

但是,我不想将“CurrentID”暴露给我的对象模型,因为它与系统的其余部分没有关系,恕我直言。在上面的SQL语句中,uo被认为是“原始”对象集,而uc被认为是系统中的当前对象。


编辑#2:

将此视为一种可能的解决方案。 http://ayende.com/blog/4196/append-only-models-with-nhibernate

老实说,我正在被愚弄,因为我正在考虑这个问题。以这种方式运行数据库,自动增量字段应该是版本字段,“id”字段应该是自动增强器的值在初始插入时的值。


答案:

我不想接受@Firo的愤怒,我不会把它从他身上移走,因为他把我带到了正确的道路上......我最终得到的是:

  1. 创建了一个基本泛型类,给出了两种类型 一个。对象的类型“ID” 湾对象本身的类型。
  2. 实例化所有类。
  3. 创建一个通用接口IRepository类,其中包含要存储/检索的对象类型。
  4. 使用要存储/检索的对象类型创建一个抽象泛型类。
  5. 为每个要存储/检索的类型创建一个具体的实现类。
  6. 在创建/更新内部,过程如下:

    Type Commit(Type item)
    {
        var clone = item.DeepClone();
        _Session.Evict(item);
        clone.Id = 0;
        clone.ProcessedDate = DateTime.Now;
        if (clone.Action.HasValue)
        {
            if (clone.Action == ProcessedAction.Create)
                clone.Action = ProcessedAction.Update;
        }
        else
        {
            clone.Action = ProcessedAction.Create;
        }
        clone.ProcessedBy = UserRepos.Where(u => u.Username == System.Threading.Thread.CurrentPrincipal.Identity.Name).First().Current;
        var savedItem = (_Session.Merge(clone) as Type);
    
        _Session.CreateQuery("UPDATE Type SET CurrentID = :newID where ID=:newID OR CurrentID=:oldID")
            .SetParameter("newID", savedItem.Id)
            .SetParameter("oldID", item.Id)
            .ExecuteUpdate();
    
        return savedItem;
    }
    
  7. 在删除方法中,我们只需更新{object} .Action = ProcessedAction.Delete

  8. 我想以另一种方式做到这一点,但意识到我们最终需要进行历史比较,我们无法要求nHibernate过滤已删除的对象,因为用户希望看到这一点。我们将创建一个业务门面来处理已删除的记录。

    再次,非常感谢@Firo对此的帮助。

    所以,尽管如此,我终于可以做到了:

    var result = {Repository}.Where(obj => obj.Id == {objectID from caller}).FirstOrDefault();
    if (result != null)
    {
        return result.Current;
    }
    else
    {
        return null;
    }
    

    并始终将当前对象返回给任何请求ID。希望它可以帮助那些处于我处境的人。

2 个答案:

答案 0 :(得分:0)

如果使用FluentNHibernate

,则在映射中

public UserMap : ClassMap<User>
{
    public UserMap()
    {
        Where("id = currentid"); // always bring back the most recent
    }
}

// in Userrepository
public void Update(User user)
{
    var clone = user.Clone();
    session.Evict(user);  // to prevent flushing the changes
    var newId = session.Save(clone);
    session.CreateQuery("UPDATE User u SET u.currentid = :current")  // <-- hql
        .SetParameter("current", newId)
        .ExecuteUpdate();
}
使用这个简单的代码,

对象图更加棘手。然后我会做以下其中一项:

  • 使用NHibernate.Envers为我存储审核信息
  • 在BL代码中明确创建新实体

我曾经看过一个仅附加模型做类似下面的事情

// UserBase is there to ensure that all others referencing the User doesnt have to update because user properties changed
class UserBase
{
    public virtual int Id { get; set; }
    public virtual ICollection<PersonDetails> AllDetails { get; private set; }
    public virtual PersonDetails CurrentDetails
    {
        get { return _currentDetauils; }
        set { _currentDetauils = value; AllDetails.Add(value); }
    }

    // same as above
    public virtual ICollection<ConfigDetails> AllConfigs { get; set; }
}

class Order
{
    public virtual int Id { get; set; }
    public virtual UserBase User { get; set; }

    public virtual IList<OrderDetail> AllDetails { get; private set; }
    public virtual IList<OrderDetail> ActiveDetails { get; private set; }

    public virtual void Add(OrderDetail detail)
    {
        AllDetails.Add(detail);
        ActiveDetails.Add(detail);
    }
    public virtual void Delete(OrderDetail detail)
    {
        detail.Active = false;
        ActiveDetails.Remove(detail);
    }
}

class OrderDetail
{
    public virtual int Id { get; set; }
    public virtual Order Parent { get; set; }

    public virtual bool Active { get; set; }
}

class OrderMap : ClassMap<Order>
{
    public OrderMap()
    {
        HasMany(o => o.AllDetails);
        HasMany(o => o.ActiveDetails).Where("active=1");
    }
}

// somewhere
public void UpdateTaxCharge(OrderDetail detail, TaxCharge charge)
{
    var clone = detail.Clone();
    clone.TaxCharge = charge;
    detail.Order.Delete(detail);
    detail.Order.Add(clone);
}

答案 1 :(得分:0)

你可以告诉NHibernate在持久化和加载实体时它应该生成什么SQL。例如,您可以告诉NHibernate使用存储过程而不是纯SQL语句。如果这是你的选择,我可以进一步阐述我的答案。