2014-01-14 4 views
0

Я пытаюсь получить обобщенную функцию CloneEntity работы с EF6.0.2Родовой Клон Ef6 Entity

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.

Вид объекта Я использую его с генерируется из Т4

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

[Update]

Я попытался

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

    } 
} 
+0

Можете ли вы показать нам определение объекта, с которым вы его используете, – Kevin

+0

спасибо, я обновил вопрос с ним. –

+0

Я предполагаю, что частичное ключевое слово отбрасывает его. Можете ли вы убедиться, что каждое определение используемого вами типа наследуется от EntityObject? – Kevin

ответ

4

Я думаю, что они дают вам один из коробки. Попробуйте что-нибудь вроде:

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

приятно, но у меня все еще есть проблема.Я обновил вопрос для описания. –

+0

Я решил проблему с ключом, установив его на прежнее значение. Проблема обсуждается на 'http: // stackoverflow.com/ questions/16646922/how-to-stop-dbentityentry-currentvalues-setvalues-try-to-change-entitykey-val' Не общий, но достаточно хороший –

0

Я думал, что добавлю свой вклад в это. Это реализация 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) 

Это мне потребовалось некоторое время, чтобы выяснить, как сделать реляционную аспект, так что надеюсь, это поможет кому-то.

+0

Это работает для DBContext? – alex

+0

Hi alex. Я не уверен, я не пробовал. Дайте ему вихрь и дайте мне знать. –

+0

Пожалуйста, если вы можете, я разместил вопрос, используя ваш код и проблемы, которые у меня есть: http://stackoverflow.com/questions/29598831/entity-framework-why-this-code-doesnt-work/29600726?noredirect= 1 # comment47365672_29600726. Можете ли вы дать мнение, если можете? Спасибо ! – alex

1
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; 
    } 
} 
+1

@ MAD Здравствуйте! Ваш код работает, чтобы скопировать объект без коллекции childs. какие изменения следует внести, чтобы скопировать одну (какую-то) дочернюю коллекцию и прикрепить к новому объекту? Спасибо ! – alex

Смежные вопросы