开发者

Fluent NHibernate - Upgrade a Class in a TPC Hierarchy

开发者 https://www.devze.com 2023-03-14 11:53 出处:网络
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

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?


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!

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号