|
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..
|
|
|
|
|
|
Hello !
I have see several methods around for deep cloning an entity framework object. All these methods use serialization. But sometimes these methods are not creating what I want.
For example I try to use the Json.net (http://www.newtonsoft.com/json[^])
to serialize and after to deserialize a object.
And after I use this code to clone :
<Extension> _
Public Function CloneJson(Of T)(source As T) As T
If [Object].ReferenceEquals(source, Nothing) Then
Return Nothing
End If
Return JsonConvert.DeserializeObject(Of T)(JsonConvert.SerializeObject(source))
End Function
But look how an object is cloned on my case :
( I want to clone a student and its Results )
Student ID......... Name........Age
1...........George........21
Course_ID..........Description Type
1.....................Math........1
Result_ID..........Course_ID......Student_ID..... Point
1......................1...............1..........30
And after the cloning :
Student ID......... Name........Age
1...........George........21
2...........George........21
Course_ID..........Description Type
1.....................Math...........1
2.....................Math...........1
Result_ID..........Course_ID......Student_ID..... Point
1......................1...............1..........30
2......................2...............2..........30
So now are 2 courses with name "Math". but I don't want to clone the courses.
How to resolve this problem ?
Thank you !
|
|
|
|
|
|
sorry , maybe you have misunderstand. The Student class has not reference to Courses. Results class has reference to courses. So as you can see I want to clone the student and its results. But each result has a reference to courses. so the deep cloning methods are duplicating the courses too.
|
|
|
|
|
Alright - but that doesn't completely invalidate my answer:
- If you nowhere else need to serialize results (at least not with Json.Net) then you could use the JsonIgnoreAttribute on the reference to the courses in the results-class.
- Else you would have to implement custom methods for cloning in the student- and results-classes for which you find pointers in the links I have provided you.
|
|
|
|
|
and sorry , but I;m thinking is there any way to have a function that take as argument an object and the child that I want to copy. and just to loop through all the property list of the object and to copy the values to a new object. The same thing with the child.
Of course I'm speaking to create a general function that will work on all similar case ( not with a specific object ).
Is there any suggestions how I can do that ?
Thank you !
|
|
|
|
|
As you have seen in the other thread, I've had a small discussion with Eddy Vluggen if his downvote on one of your replies was justified because I initially doubted it. Now I'm starting to realize what he meant by "it's a recurring theme" - because it looks like you don't thoroughly read our replies or don't evaluate provided links before asking again. You would have found the answer to your question above if you had taken a look at my link "C# Object Clone Wars", namely the section "Clone with Reflection" which includes a link to a sample.
You should work on that if you want to keep getting responses to your questions. We've answered them so far because we enjoy helping but if you don't value our answers by putting some effort into comprehending them and following provided links, it becomes a bit tiresome.
|
|
|
|
|
On your link in the clone with reflection section , contains just words.
Cloning by Reflection uses Activator.CreateInstance to create a new object of the same type, then performs a shallow copy of each field using Reflection. The advantage of this method is it's automated and does not need to be adjusted when members are added or removed from the object. Also, it can be written to provide a deep copy. The disadvantage is it uses Reflection, which is slower and not allowed in partial trust environments.
I'm asking because I don't know how to start this "Clone with reflection".
And for your and someone other opinion about my questions, well I don't care if someone down vote my questions because I'm not in this forum to collect votes. I'm not a specialist like you , and I try to make questions as I think and hope to learn something. If someone doesn't like my questions down vote and ignore them.
But I have a little opinion for you specialists ( Maybe I'm wrong , but this is my opinion ) : You judge some of my questions when I ask help how to start a thing ( like this ) and give as a response some links ( it's obvious that this are not your articles , but to someone else that wrote it ) , and on other questions where I provide my code like this http://www.codeproject.com/Messages/5036045/Make-all-the-controls-inside-a-Groupbox-unusable-w.aspx[^]
you doesn't try to give an answer at all.
If you like helping people just do it without comment , if you don't , down vote the questions and continue to collect votes for yourself.
Until I will be I this forum I will continue to make questions ( don't care If I get response or not ) , or if you have the power , close my account and relax yourself.
|
|
|
|
|
dilkonika wrote: On your link in the clone with reflection section , contains just words. For me there's a link "Sample Code" following the last sentence, leading to this Codeproject-article: Base class for cloning an object in C#[^].
My point wasn't about collecting votes, neither you nor me. My point is about valuing an answer by spending some time investigating all that is mentioned in it.
What is wrong with providing links to resources written by someone else as part of an answer if it's perfectly suitable for answering a part of your question? Though I like to help, somewhere has a cut to be made, else I would end up writing stuff again and again that has already been written.
I'm not judging your (initial) questions. So far, those that I've seen, I think they're alright. And there's also nothing wrong with asking again if something isn't understood. But asking again if something was already covered by the previous answer or would only require following a link gives the impression that the effort of writing an answer was for naught and that's kind of disappointing. If for whatever reason the link on that webpage wasn't displayed for you (JavaScript disabled?) I'm sorry to have criticized you for that but you'll have to admit that I shouldn't have to take that rather unusual phenomenon into consideration.
Also I don't regard myself an expert or specialist - it just happened that I had an answer for your current question - and I don't have an answer for your other question, otherwise I would have answered it.
|
|
|
|
|
Yes now I have read this example. But as I see he implement the Icloneable.
there are many articles on internet that suggest avoid ICloneable because of Shallow/Deep confusion.
instead , I found another way :
<pre> <Extension> _
Public Sub CopyTo(Of T)(copyFrom As T, copyTo__1 As T, copyParentProperties As Boolean)
Dim props As PropertyInfo()
If copyParentProperties Then
props = GetType(T).GetProperties()
Else
props = GetType(T).GetProperties(BindingFlags.[Public] Or BindingFlags.Instance Or BindingFlags.DeclaredOnly)
End If
Dim i As Integer = 0
While i < props.Length
Dim propertyValue = copyFrom.[GetType]().GetProperty(props(i).Name).GetValue(copyFrom, Nothing)
copyTo__1.[GetType]().GetProperty(props(i).Name).SetValue(copyTo__1, propertyValue, Nothing)
System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)
End While
End Sub
But when I try to use it like this :
newitm = New Myobject
currentobj.CopyTo(newitm, False)
context.MyObjects.Add(Newitm)
context.savechanges
I get an error on the line context.MyObjects.Add(Newitm):
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Core.dll
Additional information: Collection was modified; enumeration operation may not execute.
(But of course , if i create a new object and fill the properties manually one by one , the ADD method works without problems. )
What may be the problem ?
Thank you !
|
|
|
|
|
dilkonika wrote: there are many articles on internet that suggest avoid ICloneable because of Shallow/Deep confusion. Yes, I would also avoid it. The solution is to simply name your Clone-Method/Interface differently and let its name express what it does (shallow/deep/configurable).
Are you aware that the method CopyTo(..) makes a shallow copy and ignores fields? Basically the only advantage it has over Object.MemberwiseClone[^] is that you have the possibility to specify whether "parent" properties should be copied or not.
So if you call it on a Student, it will copy the reference to his Result(s), not make a copy of his Result(s).
dilkonika wrote:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Core.dll
Additional information: Collection was modified; enumeration operation may not execute.
I assume you used this code:
newitm = New Myobject
currentobj.CopyTo(newitm, False)
context.MyObjects.Add(Newitm)
context.savechanges
in a foreach-loop where you iterated over objects (currentobj) in context ? You can't modify a collection while you're iterating over it using an Enumerator (which is what a foreach-loop does). Your options:
- Iterate using a for-loop, if the source collection has an indexer, in reverse direction.
- Make a copy of the collection and iterate over this copy while modifying the source collection.
- Create a new collection of objects to be added/deleted while iterating the source collection. When finished, add/delete the objects contained in the new collection to/from the source collection.
|
|
|
|
|
Thank you !
No , I heaven;t used my code in a for-each loop.
currentobj=context.myobjects.firstordefault.
and after continue with the code I posted.
|
|
|
|
|
Could be a quirk in EF. Please try this (two lines switched):
newitm = New Myobject
context.MyObjects.Add(newitm)
currentobj.CopyTo(newitm, False)
context.savechanges
|
|
|
|
|
Hello !
I'm using entity framework 6 with Vb.net 2013.
I'm trying to add an extension.
This is the code :
Imports System.ComponentModel
Imports System.Collections
Imports System.Data.Entity.Core.Objects.DataClasses
Imports System.Runtime.Serialization
Imports System.IO
Imports System.Reflection
Module Extensions
<System.Runtime.CompilerServices.Extension> _
Public Function Clone(Of T As EntityObject)(source As T) As T
Dim ser = New DataContractSerializer(GetType(T))
Using stream = New MemoryStream()
ser.WriteObject(stream, source)
stream.Seek(0, SeekOrigin.Begin)
Return CType(ser.ReadObject(stream), T)
End Using
End Function
End Module
But if I try to use like this :
Dim litm, newitm as MyObject
For Each litm In itemlist
newitm = litm.Clone()
...
Next
I'm getting this error :
'Clone' is not a member of 'TheProg.MyObject'
What's the problem ?
|
|
|
|
|
Your MyObject class doesn't have a Clone() method. It's not supplied to your class automatically, after all how would a default implementation know how to correctly copy your class? It wouldn't. YOU have to implement a Clone() method yourself.
|
|
|
|
|
sorry , but why the method Clone is not added as extension ? Have I done something wrong constructing the extension ?
|
|
|
|
|
Wow an I tired. It's 2:00am here. I didn't even notice the Clone method name...
What does your MyObject class look like? Are you using Database First, Code First, or what?
|
|
|
|
|
I'm using Database First.
Partial Public Class Myobject
Public Property id As Integer
Public property name as string
Public Overridable Property chld As ICollection(Of chld) = New HashSet(Of chld)
End Class
Partial Public Class chld
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
|
|
|
|
|
It doesn't work because Database First entity objects inherit from Object , not EntityObject . Your current Close method would work just fine for a Code First EF model.
The only way to get the Clone() to work is to remove the "As EntityObject" restriction in the function header. Of course, this is going to make your Clone() method available for every type in the application.
|
|
|
|
|