流畅的NHibernate - 升级TPC层次结构中的类

时间:2011-06-22 11:59:03

标签: c# fluent-nhibernate

在我有多个继承自基类的类的情况下,在Table Per Concrete Class层次结构中,我有一种情况可能需要将一个类从较低级别“升级”到更高级别水平。

例如,我将使用课程Employee - > Manager演示。

class Employee {
   Guid Id { get; set; }
   // certain properties
}

class Manager : Employee {
   // certain unique properties
}

EmployeeMap : ClassMap<Employee> {
  // mapping information
}
ManagerMap : SubClassmap<Manager> {
  // appropriate unique properties mapping
}

var employee = new Employee { 
  Name = "Some Employee"
}

session.Save(employee);

现在,过了一会儿,EmployeeManager碰到了,现在我该怎么办? dbo.Employeesdbo.Managers是不同的表格。如何从较低级别升级到较高级别而不会丢失现有的所有内容?

1 个答案:

答案 0 :(得分:1)

不幸的是,我无法想到任何方式来巧妙地执行此更新 - 因为您使用的是Table Per Concrete Class,我能想到的唯一方法是删除现有的Employee并添加新的Manager。

话虽如此,我确实有一些可以帮助你的东西 - 我出于不同的原因需要它,但它也可以帮助你。

下面的类使用反射来提供通用的“复制”机制。要使用它,请尝试以下代码段(假设员工是晋升的员工):

var manager = new Manager;
var copier = new PropertyCopier<Employee,Manager>(employee, manager);
copier.Copy();

管理器对象现在应该具有与employee对象相同的属性值(至少在两个类中存在属性的情况下)。现在您可以提交经理并删除原始员工。

PropertyCopier类代码如下:

using System;
using System.Reflection;

// ... You will want this in your own namespace not mine. ;-)

///<summary>
/// Copies properties with the same name and type from one object to another.
///</summary>
///<typeparam name="TFirst">The object type to copy from.</typeparam>
///<typeparam name="TSecond">The object type to copy to.</typeparam>
public class PropertyCopier<TFirst, TSecond> 
    where TFirst : class
    where TSecond : class
{
    private readonly TFirst _first;
    private readonly TSecond _second;

    ///<summary>
    /// Creates an instance of the PropertyCopier.
    ///</summary>
    ///<param name="first">The object to copy properties from.</param>
    ///<param name="second">The object to copy properties to.</param>
    ///<exception cref="ArgumentNullException">An <see cref="ArgumentNullException"/> will be thrown if
    /// the source or destination objects are null.</exception>
    public PropertyCopier(TFirst first, TSecond second)
    {
        if ( first == null )
        {
            throw new ArgumentNullException("first");
        }

        if (second == null)
        {
            throw new ArgumentNullException("second");
        }

        _first = first;
        _second = second;
    }

    ///<summary>
    /// Performs the copy operation.
    ///</summary>
    public void Copy()
    {
        Copy(p => true);
    }

    ///<summary>
    /// Performs the copy operation, omitting any items for which the predicate evaluates to false.
    ///</summary>
    ///<param name="predicate">A predicate based on the <see cref="PropertyInfo"/> used to determine if the property should be copied.</param>
    ///<exception cref="ArgumentException">An <see cref="ArgumentException"/> may be thrown if the copy cannot be performed.
    /// This may happen if, for example, there is a property with the same name but a different type.</exception>
    public void Copy(Predicate<PropertyInfo> predicate)
    {
        foreach (PropertyInfo info in typeof(TFirst).GetProperties())
        {
            PropertyInfo infoInDestination = null;

            try
            {
                infoInDestination = typeof(TSecond).GetProperty(info.Name, info.PropertyType);
            }
            catch (AmbiguousMatchException)
            {
            }

            try
            {
                if (infoInDestination != null && infoInDestination.CanWrite && predicate(infoInDestination))
                {
                    infoInDestination.SetValue(_second, info.GetValue(_first, null), null);
                }
            }
            catch (Exception e)
            {
                throw new ArgumentException(String.Format("Unable to copy property called {0}", info.Name), e);
            }
        }
    }
}

希望这有帮助!