|
"REM so "clone" actually is created as just an Object and not of the same type as "element" / "entity"
This is not true because I try to use this instruction :
Ctype(element,Child1)
--- and after that everything works correctly
But as I have explained , I can't use this kind of cast because I don't have the type Child1. I have only the string "Child1".
For that I try to use CTypeDynamic.
So I want to know why this line :
System.Type.Gettype("Child1")
produce Nothing ? Because with default types like "Integer" is working.
|
|
|
|
|
dilkonika wrote: This is not true because I try to use this instruction :
--- and after that everything works correctly If we don't misunderstand each other here, this is what I would expect because you're casting element to its actual type, so CopyEntity(..) will be called for the actual type, so Dim clone As New T() will not create clone as Object but as the same type as element. So this is essentially what I presented as your first option.
The reason why Type.Gettype("Child1") doesn't work is because you have to specify more than just the name of the class, please refer to MSDN[^]. But CTypeDynamic(..) won't help you because it requires the requested target-type as a static type argument, which you don't have and won't get with Type.Gettype(..).
So it's still option 1 or 2
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
as I have read ion this link https://msdn.microsoft.com/en-us/library/ee835926(v=vs.110).aspx[^] , the type specified as parameter inside CtypeDynamic , may be a variable. Inside this link there;s an example that use Gettype inside the CTypeDynamic.
and for the type.Gettype , I read that I have to use the namespace together with my string;s name type. But where can I find the namespace for my entity object ?
|
|
|
|
|
dilkonika wrote: the type specified as parameter inside CtypeDynamic That version of CTypeDynamic(..) won't help you either because it returns Object.
dilkonika wrote: But where can I find the namespace for my entity object ? In the source file where it's declared?
Alternatively by looking at entity.GetType().FullName[^]
Why don't you try my suggested Reflection-approach?
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
For namespace I'm asking because I try to use the name where it was declared but doesn't work.
In the link for Type.Gettype , I found that I need the AssemblyQualifiedName.
Is this the namespace + the name of class ?? for this I'm confused.
|
|
|
|
|
On the page for Type.GetType are links to Type.AssemblyQualifiedName[^] where this is documented.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Yes , now I'm using this code :
Dim t a type
Dim s as string = "MyProg1.Child1,MyProg1,Version=1.0.0, Culture=neutral,PublicKeyToken=null"
t=System.Type.GetType(s)
But the problem is that the t get this value : "MyProg1.Child1" , I'm expecting to have just "Child1"
What's the problem ?
|
|
|
|
|
It's one and the same; unless you have multiple classes named "Child1" in your solution, then "Child1" may or may not be the same as "MyProg1.Child1".
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Ok , but then why when I use :
CTypeDynamic(Element,Child1)
everything is ok with my CopyEntity function.
Whe I use :
Dim t a type
Dim s as string = "MyProg1.Child1,MyProg1,Version=1.0.0, Culture=neutral,PublicKeyToken=null"
t=System.Type.GetType(s)
CtypeDynamic(Element,t)
I'm still getting the error :
An unhandled exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll
Additional information: The entity type Object is not part of the model for the current context.
|
|
|
|
|
I'm trying to replicate it.. it's not actually CTypeDynamic(Element,Child1) how your working code looks like, is it? Is it CTypeDynamic(Element, GetType(Child1)) or CTypeDynamic<Child1>(Element) ?
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Sorry Friend.
It's my mistake.
Now it's confirmed : The CloneEntity Function is working correctly when I pass the element that I get from the expression :
Dim element = CType(Enumerable.ElementAt(CallByName(MyObj1, "Child1", CallType.Get), k), Child1)
But is not working correctly when I pass the element that I get from this expression :
Dim element = CTypeDynamic(Enumerable.ElementAt(CallByName(MyObj1, "Child1", CallType.Get), k), Type.GetType("MyProg1.Child1,MyProg1,Version=1.0.0, Culture=neutral,PublicKeyToken=null"))
|
|
|
|
|
Yes, in the meantime I replicated it - both versions, simplified here:
Dim el = Enumerable.ElementAt(CallByName(MyClass, "Children", CallType.Get), 1)
Dim el1 = CType(el, ChildClass)
Dim el1copy = CopyEntity(el1)
Dim el2 = CTypeDynamic(Of ChildClass)(el)
Dim el2copy = CopyEntity(el2)
Dim el3 = CTypeDynamic(el, GetType(ChildClass))
Dim el3copy = CopyEntity(el3) That's basically the same as your code above. And it's still the same as what I'm talking about for the last messages
I know you can't use 1 and 2. The reason they work is because the return type of CType(el, ChildClass) and CTypeDynamic(Of ChildClass)(el) is ChildClass. The reason 3 doesn't work is because the return type of CTypeDynamic(el, GetType(ChildClass)) is Object (so it doesn't have anything to do with Type.GetType(..)). So, for 1 and 2 CopyEntity(..) is called for the correct generic type, for 3 it's called with Object as generic type. I don't know if there's a way to make 3 work. I tested the same thing (as 3) in C# and it actually works. Either VB differs there from C# or I just don't know how to do it in VB. I can assure you though that the reflection-approach that I suggested would work. If you want to find out if there is a way to make 3 work I would suggest you post a new question so that someone who knows VB better than me will be more likely to notice.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Thank you !
Please , can you post here the code ( of variant 3 ) that works on C# ? ( I mean the whole code that you have tested , with the Copyentity function too ).
Thank you !
|
|
|
|
|
Sure, here you go:
class ChildClass
{ }
class MyClass
{
public ICollection<ChildClass> Children { get; set; }
public MyClass()
{
Children = new HashSet<ChildClass>() { new ChildClass() };
}
}
T CopyEntity<T>(T entity)
where T : class, new()
{
T copy = new T();
return copy;
}
void Test()
{
MyClass myClass = new MyClass();
dynamic children = typeof(MyClass).GetProperty("Children").GetValue(myClass);
dynamic child = Enumerable.ElementAt(children, 0);
dynamic copy = CopyEntity(child);
}
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
modified 15-Apr-15 20:35pm.
|
|
|
|
|
Thank you !
But your code in C# when is converted in vb.net ( with code converters ) , is :
Class ChildClass
End Class
Class [MyClass]
Public Property Children() As ICollection(Of ChildClass)
Get
Return m_Children
End Get
Set
m_Children = Value
End Set
End Property
Private m_Children As ICollection(Of ChildClass)
Public Sub New()
Children = New HashSet(Of ChildClass)() From { _
New ChildClass() _
}
End Sub
End Class
Private Function CopyEntity(Of T As {Class, New})(entity As T) As T
Dim copy As New T()
' actual copying omitted here
' for testing only the return type matters
Return copy
End Function
Private Sub Test()
Dim [myClass] As New [MyClass]()
Dim children As dynamic = GetType([MyClass]).GetProperty("Children").GetValue([myClass])
Dim child As dynamic = Enumerable.ElementAt(children, 0)
Dim copy As dynamic = CopyEntity(child)
' copy is of type ChildClass
End Sub
and this code is not using CTypeDynamic. So what you have tested in vb.net that doesn't work ?
and on my vb.net code , the error came up on the line :
Dim en = ctx.Entry(clone)
and you don't have a such line in your code.
.............................
Also I try to implement your version with reflection , but I can't make it work on vb.net ? can you provide some more
help ?
..............................
Thank you !
modified 16-Apr-15 1:20am.
|
|
|
|
|
dilkonika wrote: and this code is not using CTypeDynamic. Because it works without it. For completeness, I included the analog into the VB-version:
Public Class ChildClass
End Class
Public Class SomeClass
Public Property Children As ICollection(Of ChildClass) = New HashSet(Of ChildClass)
End Class
Public Function CopyEntity(Of T As {Class, New})(entity As T) As T
Dim clone As New T()
Return clone
End Function
Sub Main()
Dim someClass = New SomeClass
someClass.Children.Add(New ChildClass)
Dim el = Enumerable.ElementAt(CallByName(someClass, "Children", CallType.Get), 0)
Dim elcopy = CopyEntity(el)
Dim el1 = CType(el, ChildClass)
Dim el1copy = CopyEntity(el1)
Dim el2 = CTypeDynamic(Of ChildClass)(el)
Dim el2copy = CopyEntity(el2)
Dim el3 = CTypeDynamic(el, GetType(ChildClass))
Dim el3copy = CopyEntity(el3)
End Sub
Quote: and on my vb.net code , the error came up on the line :
Dim en = ctx.Entry(clone)
and you don't have a such line in your code. Because the reason why it not works (when it not works) is because clone is of type Object and not of type ChildClass. So I only have to check for the type of clone to know if your line ctx.Entry(clone) would work.
dilkonika wrote: Also I try to implement your version with reflection , but I can't make it work on vb.net ? can you provide some more
help ? Please post what you have so far.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Thank you !
But for the Reflection version , I just converted the code to vb.net but I don't know how to integreate with my code. If you can just post your full version for C# with reflection so I can understand more ( I don't have so much knowledge about reflection , but at this point I really need a solution for my problem , so I need to use it )
|
|
|
|
|
class Program
{
class ChildClass
{ }
class SomeClass
{
public ICollection<ChildClass> Children { get; set; }
public SomeClass()
{
Children = new HashSet<ChildClass>() { new ChildClass() };
}
}
public static T CopyEntity<T>(T entity)
where T : class, new()
{
T copy = new T();
return copy;
}
static void Main(string[] args)
{
SomeClass someClass = new SomeClass();
dynamic children = typeof(SomeClass).GetProperty("Children").GetValue(someClass);
object child = Enumerable.ElementAt(children, 0);
MethodInfo mi = typeof(Program).GetMethod("CopyEntity", BindingFlags.Static | BindingFlags.Public);
mi = mi.MakeGenericMethod(new Type[] { child.GetType() });
object clone = mi.Invoke(null, new object[] { child });
}
}
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Thank you !
Your code is working.
Just for curiosity , the other variant is working too.
I found this solution in vb.net , to make a change on CopyEntityFunction like this :
instead of
Dim clone as New(T)
I use this :
Dim clone = Activator.CreateInstance(entity.GetType())
Now it's working.
Thank you for your help.
|
|
|
|
|
dilkonika wrote: Dim clone = Activator.CreateInstance(entity.GetType()) Yes, that's a simpler solution if you don't need CopyEntity to be generic (that is, if you don't need the static type T for anything else). If you haven't already, you can remove that generic stuff from the declaration of CopyEntity then.
dilkonika wrote: Thank you for your help. You're welcome.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Hello !
This is a code that is supposed to do a copy of an entity framework object with the childs collection that the user want.
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 use the above code like that :
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 problem is that nothing is copied.
What's wrong with my code ?
Thank you !
|
|
|
|
|
Please include your class-definition of MyObject (you can omit the methods, if it has any).
Did you already observe it in debug-mode?
|
|
|
|
|
Thank you !
I put some breakpoints , and I have detected that maybe 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 ?
also I did another test ( maybe a stupid test but anyway ) :
I put the line info.SetValue(result, info.GetValue(entity, Nothing), Nothing)
after the next so outside the for loop.
Now the entity is copied , but not the child that is included on inc variable. ?
The code is supposed to work will any kind of classes , but anyway this are the classes where I made the tests:
Partial Public Class Myobject
Public Property id As Integer
Public property name as string
Public Overridable Property Child_list As ICollection(Of Child_list) = New HashSet(Of Child_list)
End Class
Partial Public Class Child_list
Public Property id As Integer
Public Property date1 as DateTime
Public Property quantity as Integer
Public Property ParentID as integer
Public Overridable Property MyObj1 As MyObject
End Class
|
|
|
|
|
Correct, the code you have there is expecting an EdmScalarPropertyAttribute on each field or property that should be copied (with the exception of EntityCollections). You didn't decorate the properties of your class with this attribute, so the SetValue(..)-line was skipped.
Note: I'm not familiar with EF. My following advice should make your CloneEntity-code work (or at least "almost working") but someone who knows about EF might have more to say about it. In particular, I have no idea where the id of your classes gets initialized; if you have to deal with that yourself or if it's somehow magically done for you.
That should be it I think..
|
|
|
|
|
|