0

I'm using Entity Framework 6.0, DbContext. I'm using this method to copy an object and some related children:

Imports System.Data.Objects
Imports System.Data.Objects.DataClasses
Imports System.Runtime.CompilerServices

Public Module Entities
<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
If include Is Nothing Then include = New List(Of IncludeEntity)()
Dim myType = entity.GetType()
Dim methodInfo = context.GetType().GetMethod("CreateObject").MakeGenericMethod(myType)
Dim result = methodInfo.Invoke(context, Nothing)
Dim propertyInfo = entity.GetType().GetProperties()
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
    If info.PropertyType.Name.Equals("EntityCollection`1", StringComparison.OrdinalIgnoreCase) Then
        Dim shouldInclude = include.SingleOrDefault(Function(i) i.Name.Equals(info.Name, StringComparison.OrdinalIgnoreCase))
        If shouldInclude Is Nothing Then Continue For
        Dim relatedChildren = info.GetValue(entity, Nothing)
        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)
        For Each child In relatedChildren
            Dim cloneChild = CloneEntityHelper(child, context, shouldInclude.Children, shouldInclude.CopyKeys)
            children.Add(cloneChild)
        Next
        info.SetValue(result, children, Nothing)
    End If
Next

Return result
End Function

Public Class IncludeEntity
    Public Property Name As String

    Public Property Children As New List(Of IncludeEntity)

    Public Property CopyKeys As Boolean
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

Now I'm using the code like below :

Dim litm, newitm As New MyObject
    Dim inc = New List(Of IncludeEntity)()
    inc.Add(New IncludeEntity("Child_list"))
    litm=context.MyObjects.FirstOrDefault
    newitm = litm.CloneEntity(CType(context, Entity.Infrastructure.IObjectContextAdapter).ObjectContext,include:=inc)

The code is executed without errors, but nothing is copied, so newitm is empty.

I have inspected the code and found that this line on the CloneEntity function :

Dim myType = entity.GetType()

Is producing a strange type.

I'm expecting that the type will be of MyObject type, but instead this return :

MyObject_F2FFE64DA472EB2B2BDF7E143DE887D3845AD9D1731FD3107937062AC0C2E4BB

This line too :

Dim result = methodInfo.Invoke(context, Nothing)

produces the same strange type.

I don't know if this is the problem, but this is the only strange thing I have noticed.

Can you help me to find out why this code doesn't work?

Thank you!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
alex
  • 694
  • 3
  • 15
  • 35

1 Answers1

1

Entity framework, like many other ORMs will build a proxy type for your entities so that it can intercept calls to:

  • Lazy load the contents of any collection contained as properties within your entity, when you access those collection properties.
  • Detect that you have made changes to the properties of an instance as part of dirty checking, so that it will know which objects are dirty and need to be saved to the database when you invoke SaveChanges.

Refer for example to EF returning proxy class instead of actual entity or Working with Proxies.

If you want to find out the underlying type of your entity that is wrapped by the proxy, i.e. the one that would match with the type you are looking for (e.g. MyObject), you can do that using a method in the object context:

var underlyingType = ObjectContext.GetObjectType(entity.GetType());
Community
  • 1
  • 1
Alex
  • 13,024
  • 33
  • 62
  • Thank you , I have used your code , but now is a line that still continue to keep the proxy. The line : Dim result = methodInfo.Invoke(context, Nothing) , return MyObject_F2FFE64DA472EB2B2BDF7E143DE887D3845AD9D1731FD3107937062AC0C2E4BB. And my code still doesn;t copy anything. so the problem is that I have noticed , or other ? – alex Apr 13 '15 at 15:00
  • Did you replace `Dim myType = entity.GetType()` with `Dim myType = ObjectContext.GetObjectType(entity.GetType())` in your code? Because then it should obtain the method info for `context.CreateObject()`. when you call `Dim methodInfo = context.GetType().GetMethod("CreateObject").MakeGenericMethod(myType)` – Alex Apr 13 '15 at 15:16
  • yes , I have replaced , but still the line Dim result = methodInfo.Invoke(context, Nothing) , return MyObject_F2FFE64DA472EB2B2BDF7E143DE887D3845AD9D1731FD3107937062AC0C2E4BB – alex Apr 13 '15 at 16:08
  • Ahh, I understand. Yes a call to `contect.CreateObject()` will also return a proxy instance. Note that it is possible to disable proxying in the constructor of your db context, using `Configuration.ProxyCreationEnabled = false` – Alex Apr 13 '15 at 16:12
  • yes , but I;m using lazyloading and I need this. It is possible to use your code temporary and after to re enabled again ? – alex Apr 13 '15 at 16:16
  • and do you think these proxies are the problem that my code doesn;t work , or is something else ? Because all the actions that I do with entity objects works correctly , only this clone function doesn;t work. – alex Apr 13 '15 at 16:21
  • You mentioned that the call to `Dim result = methodInfo.Invoke(context, Nothing)` returns a non-null proxy instance (of type `MyObject_F2....`). At the end of the `CloneEntityHelper` function, you do `return result`. So it should contain the object, and not be null (`Nothing`), and consequently the same should be true for `newitm`. Check when `result` in your `CloneEntityHelper` becomes null. – Alex Apr 13 '15 at 16:52
  • I didn;t say itls a Null. It's a Proxy object MyObject_F2FFE64DA472EB2B2BDF7E143DE887D3845AD9D1731FD3107937062AC0C2E4BB that have all the properties Nothing. So I said that nothing is copied. – alex Apr 13 '15 at 17:05
  • So at the end of CloneHelperEntity , the result it's a proxy object that have all the properties with values of Nothing. the same it's the newitm.I did another test : Before running the clone process , I disable The ProxyCreationEnabled. Now the results that return the CloneHelperEntity is a normal object ( MyObject) , but still has all the properties with Nothing Value.The same thing after with newitm. so again nothing is copied. – alex Apr 13 '15 at 17:11
  • I detect that the problem may be this line : Dim attributes = info.GetCustomAttributes(GetType(EdmScalarPropertyAttribute), False).ToList() . The attributes always have the Length=0 , so all the codes inside the For Each info In propertyInfo doesn;t get executed , and goes to next for each info and so at the end the result is an empty object. What can I do ? – alex Apr 13 '15 at 17:21
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/75145/discussion-between-alex-and-alex). – Alex Apr 13 '15 at 18:57