คัดลอกวัตถุเอนทิตีไปยังวัตถุ POCO โดยที่ Entity ints คือ Enum บน POCO

ฉันกำลังพยายามสร้างเครื่องถ่ายเอกสารโดยใช้สำเนาทรัพย์สินของ Jon Skeets มันทำงานได้ดีกับคุณสมบัติทั้งหมด แต่ไม่ใช่การแจงนับ ฉันได้ลองหลายครั้งในการเปลี่ยนวิธีการทำงานเพื่อแจงนับให้ประสบความสำเร็จเพียงเล็กน้อย ฉันสงสัยว่าใครอาจมีความคิดในการทำเช่นนี้

Jon Skeets ต้นฉบับ โดยการแก้ไขของฉันถูกแบ่งออกพร้อมความคิดเห็นในวิธี BUILDCOPIER

เรียกร้องให้นี่คือ

        var result = Common.PropertyCopy<POCO>.CopyFrom(Entity);

รหัสจอน Skeet ดั้งเดิม

/// <summary>
/// Generic class which copies to its target type from a source
/// type specified in the Copy method. The types are specified
/// separately to take advantage of type inference on generic
/// method arguments.
/// http://www.yoda.arachsys.com/csharp/miscutil/
/// </summary>
public static class PropertyCopy<TTarget> where TTarget : class, new()
{
    /// <summary>
    /// Copies all readable properties from the source to a new instance
    /// of TTarget.
    /// </summary>
    public static TTarget CopyFrom<TSource>(TSource source) where TSource : class
    {
        return PropertyCopier<TSource>.Copy(source);
    }

    /// <summary>
    /// Static class to efficiently store the compiled delegate which can
    /// do the copying. We need a bit of work to ensure that exceptions are
    /// appropriately propagated, as the exception is generated at type initialization
    /// time, but we wish it to be thrown as an ArgumentException.
    /// </summary>
    private static class PropertyCopier<TSource> where TSource : class
    {
        private static readonly Func<TSource, TTarget> copier;
        private static readonly Exception initializationException;

        internal static TTarget Copy(TSource source)
        {
            if (initializationException != null)
            {
                throw initializationException;
            }
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }
            return copier(source);
        }

        static PropertyCopier()
        {
            try
            {
                copier = BuildCopier();
                initializationException = null;
            }
            catch (Exception e)
            {
                copier = null;
                initializationException = e;
            }
        }

        private static Func<TSource, TTarget> BuildCopier()
        {
            ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
            var bindings = new List<MemberBinding>();
            foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties())
            {
                if (!sourceProperty.CanRead)
                {
                    continue;
                }
                PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name);
                if (targetProperty == null)
                {
                    throw new ArgumentException("Property " + sourceProperty.Name + " is not present and accessible in " + typeof(TTarget).FullName);
                }
                if (!targetProperty.CanWrite)
                {
                    throw new ArgumentException("Property " + sourceProperty.Name + " is not writable in " + typeof(TTarget).FullName);
                }

                // THIS IS FALSE FOR SOURCE(INT) TARGET ENUMS
                if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                {
                    //ADDED FOLLOWING TO HANDLE COPY FROM INT TO ENUM
                    /////////////////////////////////////////////////////////////////////////////////////////////////////
                    // Special Case because Entities are created with property as ints, not enum types
                    if (targetProperty.PropertyType.IsEnum && (sourceProperty.PropertyType == typeof(int)))
                    {
                        var expressionparam = Expression.Parameter(sourceProperty.PropertyType);
                        // cast the entity source as the enum target
                        var cast = Expression.Convert(expressionparam, targetProperty.PropertyType);
                        // add to the binding tree
                        bindings.Add(Expression.Bind(targetProperty, Expression.Property(cast, sourceProperty)));
                        continue;
                    }
                    /////////////////////////////////////////////////////////////////////////////////////////////////////

                    throw new ArgumentException("Property " + sourceProperty.Name + " has an incompatible type in " + typeof(TTarget).FullName);
                }
            Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
            return Expression.Lambda<Func<TSource, TTarget>>(initializer, sourceParameter).Compile();
        }
    }
}

เอนัม

public enum NotificationType
{
    InAppNotificiation = 0,
    EmailNotification,
    SMS
}

คลาสเอนทิตีที่สร้างโดย EF

public class Entity
{
    public int ProcessedStatus { get; set; }
    public int Priority { get; set; }
    public System.Guid NotifyToUserId { get; set; }
    public string NotifyFrom { get; set; }
    public string NotifySubject { get; set; }
    public string NotifyMessageBody { get; set; }
    public int NotificationType { get; set; }  <-- Stored as int in DB

     public virtual MercuryUser MercuryUser { get; set; } <--complex type
}

คลาส POCO

public class POCO
{
    public int ProcessedStatus { get; set; }
    public int Priority { get; set; }
    public System.Guid NotifyToUserId { get; set; }
    public string NotifyFrom { get; set; }
    public string NotifySubject { get; set; }
    public string NotifyMessageBody { get; set; }
    public NotificationType NotificationType { get; set; }  <-- ENUM TYPE

    public MyUser MyUser { get; set; } <-- complex type
}

มีข้อยกเว้นเกิดขึ้นที่บรรทัด

bindings.Add(Expression.Bind(targetProperty, Expression.Property(cast, sourceProperty)));

คุณสมบัติ 'Int32 NotificationType' ไม่ได้ถูกกำหนดไว้สำหรับประเภท 'Models.Enums.NotificationType


person DRobertE    schedule 08.07.2014    source แหล่งที่มา
comment
EF รองรับ POCO และ enums ที่เน้นโค้ดเป็นหลัก (ตั้งแต่เวอร์ชัน 5) ดังนั้นคุณจึงไม่จำเป็นต้องคัดลอกออบเจ็กต์หนึ่งไปยังอีกออบเจ็กต์อีกต่อไป   -  person Panagiotis Kanavos    schedule 09.07.2014
comment
การตัดสินใจออกแบบเกิดขึ้นก่อนที่ฉันจะมาที่โปรเจ็กต์และไม่ได้ใช้โค้ดก่อน   -  person DRobertE    schedule 09.07.2014
comment
คุณไม่จำลองโค้ดก่อนอย่างมีประสิทธิภาพเมื่อคุณเขียนคลาสเป้าหมายใช่หรือไม่ อย่างไรก็ตาม คุณสามารถใช้ไลบรารีการแมป เช่น AutoMapper เพื่อแมป DTO หนึ่งไปยังอีก DTO โดยไม่ต้องเขียนโค้ดการสะท้อนด้วยตัวเอง การแมปไลบรารียังดูแลการแมปแคชด้วย ดังนั้นคุณไม่จำเป็นต้องค้นหาคุณสมบัติซ้ำทุกครั้งที่คุณต้องการแมปวัตถุใหม่   -  person Panagiotis Kanavos    schedule 09.07.2014


คำตอบ (1)


ดังนั้นฉันจึงพบโพสต์นี้ C# การใช้ Reflection เพื่อคัดลอกคุณสมบัติคลาสพื้นฐาน และลองสิ่งนี้แทน บางครั้งก็เรียบง่ายก็ใช้งานได้... ไม่แน่ใจว่าทำไมอันนี้ถึงตั้งค่า enum และยังไม่ระเบิดในประเภทที่ซับซ้อน บางทีอาจเป็นเพราะนิพจน์แลมบ์ดาตีความวัตถุอย่างไร มันไม่ได้ตรวจสอบว่าสามารถมอบหมายจากสิ่งใดๆ ได้ โดยเฉพาะประเภท enum เพียงตั้งค่าตาม int และชื่อคุณสมบัติ ประเภทของตัวนับที่ใช้งานง่ายหาก isAssignable บอกว่า NO เมื่อพยายามตั้งค่าจากคุณสมบัติแหล่งที่มาของประเภท int และคุณสมบัติเป้าหมายของประเภท enum เมื่อประเภทพื้นฐานเหมือนกัน หากใครสามารถให้ข้อมูลเชิงลึกเกี่ยวกับเรื่องนี้ได้ก็จะดีมาก ตอนนี้ฉันจะใช้วิธีการคัดลอกที่ง่ายกว่าตามรายการด้านล่าง ข้อเสียคือมันไม่แคชเครื่องถ่ายเอกสารจึงต้องผ่านมันทุกครั้ง

    public static T1 CopyFrom<T1, T2>(T1 obj, T2 otherObject) where T1 : class where T2 : class
    {
        PropertyInfo[] srcFields = otherObject.GetType().GetProperties(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);

        PropertyInfo[] destFields = obj.GetType().GetProperties(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty);

        foreach (var property in srcFields)
        {
            var dest = destFields.FirstOrDefault(x => x.Name == property.Name);
            if (dest != null && dest.CanWrite)
                dest.SetValue(obj, property.GetValue(otherObject, null), null);
        }

        return obj;
    }
person DRobertE    schedule 09.07.2014