【问题标题】:Generic Clone of EF6 EntityEF6 实体的通用克隆
【发布时间】:2014-01-14 02:29:29
【问题描述】:

我正在尝试让通用 CloneEntity 函数与 EF6.0.2 一起使用

public static T CopyEntity<T>(MyContext ctx, T entity, 
bool copyKeys = false) where T : EntityObject
{
T clone = ctx.CreateObject<T>();
PropertyInfo[] pis = entity.GetType().GetProperties();

foreach (PropertyInfo pi in pis)
{
    EdmScalarPropertyAttribute[] attrs = (EdmScalarPropertyAttribute[])
                  pi.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false);

    foreach (EdmScalarPropertyAttribute attr in attrs)
    {
        if (!copyKeys && attr.EntityKeyProperty)
            continue;

        pi.SetValue(clone, pi.GetValue(entity, null), null);
    }
}

return clone;
}

This code comes from here

[更新] 我不得不将声明更改为

public static T CloneEntity<T>(T entity, bool copyKeys = false) where T : class

这解决了一个编译错误:“T”必须是引用类型才能在泛型类型或方法中用作参数“T”

但是,当我使用此方法时,不会将任何属性复制到新对象。

我没有先使用代码。我使用的对象是从 edmx 文件生成的。

我使用的对象类型是从 T4 生成的

public partial class job
{
    public short LineID { get; set; }
    public short CycleID { get; set; }
    // other fields 
}

我的 DBContext 是这样的

public partial class DataEntities : DbContext
{

    public DbSet<job> Jobs { get; set; }
}

[更新]

我试过了

using (var db = CreateDb())
{

    var nJob = new job();
    db.jobs.Attach(nJob);
    db.Entry(nJob).CurrentValues.SetValues(job);

    return nJob;
}

但我得到一个错误

"The property 'JobID' is part of the object's key information and cannot be modified. "

上下文是一个部分类

还有

partial class DataEntities
{
    public DataEntities(string efConnectString)
        : base(efConnectString)
    {

    }
}

【问题讨论】:

  • 你能告诉我们你正在使用它的对象的定义吗
  • 谢谢,我用它更新了问题。
  • 我猜部分关键字正在抛弃它。你能确保你使用的类型的每个定义都继承自 EntityObject 吗?
  • 这里他们谈论你的错误:stackoverflow.com/questions/6451120/…
  • @Kevin 实体是从 T4 生成的,您是否建议我更改模板?

标签: entity-framework


【解决方案1】:

我认为他们会给你一个开箱即用的。尝试类似:

context.Entry(MyNewEntity).CurrentValues.SetValues(MyOldEntity);

【讨论】:

  • 很好,但我还是有问题。我更新了要描述的问题。
  • 我通过将密钥设置为旧值解决了这个问题。这个问题在http://stackoverflow.com/questions/16646922/how-to-stop-dbentityentry-currentvalues-setvalues-trying-to-change-entitykey-val 讨论 不是通用的,但足够好
【解决方案2】:
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;
    }
}

【讨论】:

  • @MAD 你好!您的代码可以复制没有子集合的实体。为了复制一个(或一些)子集合并附加到新对象,我应该进行哪些更改?谢谢!
【解决方案3】:

我想我会为此添加我的贡献。它是一个 VB 实现,是对代码项目中现有代码的补充。

此实现允许包含关系属性(但您必须指定这一点)。

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

以及一个示例用法:

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)

我花了一段时间才弄清楚如何处理关系方面,所以希望这会对某人有所帮助。

【讨论】:

  • 嗨,亚历克斯。我不确定,我没试过。试一试,让我知道。
  • 拜托,如果可以的话,我使用您的代码发布了一个问题以及我遇到的问题:stackoverflow.com/questions/29598831/…。可以的话能给个意见吗?谢谢!
猜你喜欢
  • 1970-01-01
  • 2023-03-15
  • 2012-07-22
  • 2016-03-17
  • 1970-01-01
  • 2010-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多