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

发布于 2024-11-16 21:32:45 字数 705 浏览 1 评论 0原文

在给定的情况下,我有多个从基类继承的类,在每个具体类的表层次结构中,我可能需要从较低级别“升级”类到一个更高的水平。

作为一个例子,我将使用一个类 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);

那么,过了一会儿,Employee 被提升为 Manager,那么现在我能做什么呢? dbo.Employeesdbo.Managers 是不同的表。如何从较低级别升级到较高级别而不丢失现有级别的所有内容?

In a given situation where I have multiple classes that inherit from a base class, in a Table Per Concrete Class Hierarchy, I have a circumstance where it may be neccessary to 'upgrade' a class from a lower level to a higher level.

As an example, I will use a class Employee -> Manager demonstration.

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);

Now then, a while later, that Employee gets bumped to Manager, so now what can I do? dbo.Employees and dbo.Managers are different tables. How can I upgrade from a lower class to a higher one without losing everything attached to the existing one?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

始终不够 2024-11-23 21:32:45

不幸的是,我想不出任何方法来巧妙地执行此更新 - 因为您正在使用每个具体类的表,我能想到的唯一方法是删除现有的员工并添加新的经理。

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

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

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

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

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);
            }
        }
    }
}

希望这有帮助!

Unfortunately, I can't think of any way to perform this update neatly - because you are using the Table Per Concrete Class, the only way I can think of is to remove the existing Employee and add a new Manager.

Having said that, I do have something that may help you - I needed it for a different reason, but it may help in your case too.

The class below uses reflection to provide a generic "copy" mechanism. To use it, try the following code snippet (assuming employee is the employee getting promoted):

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

The manager object should now have the same property values as the employee object did (at least where the property exists in both classes). Now you can commit the manager and delete the original employee.

The PropertyCopier class code is as follows:

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);
            }
        }
    }
}

Hope this helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文