Generic Clone of EF6 Entity

后端 未结 3 1938
没有蜡笔的小新
没有蜡笔的小新 2021-01-27 11:16

I am trying to get a generic CloneEntity function working with EF6.0.2

public static T CopyEntity(MyContext ctx, T entity, 
bool copyKeys = false) where         


        
相关标签:
3条回答
  • 2021-01-27 11:22

    I thought I would add my contribution to this. It is a VB implementation, and an addition to existing code found on code project.

    This implementation allows for the relational properties to be included (but you have to specify this).

    Imports System.Data.Objects
    Imports System.Data.Objects.DataClasses
    Imports System.Runtime.CompilerServices
    
    
    Public Module Entities
    
    ''' <summary>
    ''' Clone an entity
    ''' </summary>
    ''' <remarks>
    ''' Inspiration from: http://www.codeproject.com/Tips/474296/Clone-an-Entity-in-Entity-Framework-4
    ''' </remarks>
    <Extension()>
    Public Function CloneEntity(Of T As Class)(entity As T, context As ObjectContext, Optional include As List(Of IncludeEntity) = Nothing, Optional copyKeys As Boolean = False) As T
        Return CloneEntityHelper(entity, context, include, copyKeys)
    End Function
    
    Private Function CloneEntityHelper(Of T As Class)(entity As T, context As ObjectContext, Optional include As List(Of IncludeEntity) = Nothing, Optional copyKeys As Boolean = False) As T
        ' Set default parameter values
        If include Is Nothing Then include = New List(Of IncludeEntity)()
        'If visited Is Nothing Then visited = New List(Of String)()
    
        ' Get the type of entity we are dealing with
        Dim myType = entity.GetType()
    
        ' Make a copy of this object
        Dim methodInfo = context.GetType().GetMethod("CreateObject").MakeGenericMethod(myType)
        Dim result = methodInfo.Invoke(context, Nothing)
    
        ' Get the property information for the source object
        Dim propertyInfo = entity.GetType().GetProperties()
    
        ' Copy over the property information
        For Each info In propertyInfo
            Dim attributes = info.GetCustomAttributes(GetType(EdmScalarPropertyAttribute), False).ToList()
    
            For Each attr As EdmScalarPropertyAttribute In attributes 
                If (Not copyKeys) AndAlso attr.EntityKeyProperty
                    Continue For
                End If
    
                info.SetValue(result, info.GetValue(entity, Nothing), Nothing)
            Next
    
            ' Handle relational properties
            If info.PropertyType.Name.Equals("EntityCollection`1", StringComparison.OrdinalIgnoreCase) Then
                ' Determine whether or not we are allowed to deal with this relationship
                Dim shouldInclude = include.SingleOrDefault(Function(i) i.Name.Equals(info.Name, StringComparison.OrdinalIgnoreCase))
                If shouldInclude Is Nothing Then Continue For
    
                ' Clone the property
                Dim relatedChildren = info.GetValue(entity, Nothing)
    
                ' Get an EntityCollection instance to hold the relational entries
                Dim propertyType As Type = relatedChildren.GetType().GetGenericArguments().First()
                Dim genericType As Type = GetType(EntityCollection(Of ))
                Dim boundType = genericType.MakeGenericType(propertyType)
                Dim children = Activator.CreateInstance(boundType)
    
                ' Load the children into the property
                For Each child In relatedChildren
                    Dim cloneChild = CloneEntityHelper(child, context, shouldInclude.Children, shouldInclude.CopyKeys)
                    children.Add(cloneChild)
                Next
    
                ' Save this value
                info.SetValue(result, children, Nothing)
            End If
        Next
    
        Return result
    End Function
    
    
    ''' <summary>
    ''' Represent which (relational) properties should be included
    ''' </summary>
    Public Class IncludeEntity
        ''' <summary>
        ''' Name of the relationship to include
        ''' </summary>
        Public Property Name As String
    
        ''' <summary>
        ''' Relationships to include from the selected property
        ''' </summary>
        Public Property Children As New List(Of IncludeEntity)
    
        ''' <summary>
        ''' Whether or not to Copy keys
        ''' </summary>
        Public Property CopyKeys As Boolean
    
        ''' <summary>
        ''' Empty Constructor
        ''' </summary>
        Public Sub New()
        End Sub
    
        ''' <summary>
        ''' Create with single children
        ''' </summary>
        Public Sub New(propertyName As String, ParamArray childNodes() As String)
            Name = propertyName 
            Children = childNodes.Select(Function(n) new IncludeEntity(n)).ToList()
        End Sub
    End Class
    End Module
    

    and an example usage:

    Dim formToClone = con.SF_Forms.FirstOrDefault(Function(e) e.Form_id = f.Id)
    
    ' Define what should be copied
    Dim inc = New List(Of IncludeEntity)()
    
    Dim validation = New IncludeEntity("SF_Questions_validation", "SF_Validation_Parameters")
    
    Dim questions = new IncludeEntity("SF_Questions", "SF_Question_Parameters")
    questions.Children.Add(validation)
    
    Dim questionGroups = new IncludeEntity("SF_Question_Groups")
    questionGroups.Children.Add(questions)
    
    Dim actions = New IncludeEntity("SF_Actions", "SF_Action_Parameters")
    
    inc.Add(questionGroups)
    inc.Add(actions)
    inc.Add(new IncludeEntity("SF_Messages"))
    
    ' Save the cloned form
    Dim clonedForm = formToClone.CloneEntity(con, include := inc)
    

    It took me a while to work out how to do the relational aspect, so hopefully this will help someone.

    0 讨论(0)
  • 2021-01-27 11:30
    public class EntityHelper
    {
        public static T CopyEntity<T>(MCEntities ctx, T entity, bool copyKeys = false) where T : class, new()
        {
            T clone = new T();
            var en = ctx.Entry(clone);
            en.State = System.Data.Entity.EntityState.Added;
            ctx.Entry(clone).CurrentValues.SetValues(entity);
            en.State = System.Data.Entity.EntityState.Detached;
            return clone;
        }
    }
    
    0 讨论(0)
  • 2021-01-27 11:31

    I think they give you one out of the box. Try something like:

    context.Entry(MyNewEntity).CurrentValues.SetValues(MyOldEntity);
    
    0 讨论(0)
提交回复
热议问题