Entity Framework - Create a copy of an object and remove an element in a copy's collection removes the element on both the original and the copy - entity-framework

I want to clone an object obtained from the db context using EF. Such object contains a collection that in turn contains another one.
For the copy or clone of the object, I have a method that creates a new instance, copies all the fields and finally marks the original one as EntityState.Unchaged.
Availability newAvailability = new Availability();
newAvailability = availabilityRepository.Copy(sourceAvailability, user);
newAvailability.RemoveTag(img, tagToRemove);
In the example, I have an availability which contains a set of images where each of them can have tags. What I want to do is to create a clone of the Availability object along with the images, then associate the tags to each image by using a join table.
In a next step, I want to remove a tag from one of the copied images, from the copied Availability.
The result is that the tag is being removed on both arrays.
What is weird, is that RemoveTag extension method doesn't use EF at all. It looks like this:
var filterExpression = new Func<Image, bool>(y => y.Url == img.Url));
var image = availability.Images.FirstOrDefault(filterExpression);
image.Tags.Remove(tag);
return availability;
Now, if I have an Availability like this (assume this the result of joining tables, an availability can have many images and an image can have many tags):
A = 1 ; Image = 1; Tag = 6
and then when I create its copy it looks like:
A = 2 ; Image = 2; Tag = 6
the result should be that the Tag 6 in Image 2 from the Availability gets removed and the source Availability is kept unchanged. But that's not what is happening.
Note: the Copy method is doing at some point
this.context.Entry(availability).State = EntityState.Unchanged;
What could I be doing wrong? How should I avoid EF from removing this child object from the source parent?
Thanks in advance
EDIT:
Found the solution, although I'm not entirely sure what changes internally on doing this. I wrote
imgCopy.Tags = img.Tags;
but when I changed to
img.Tags.ToList().ForEach((_) =>
{
imgCopy.Tags.Add(_);
});
the problem was solved.

Related

Moving Wagtail pages in a migration

I'm restructuring my Wagtail app to remove an IndexPage that only has a single item in it, and moving that item to be a child of the current IndexPage's parent.
basically moving from this:
Page--|
|--IndexPage--|
|--ChildPages (there's only ever 1 of these)
to this:
Page--|
|--ChildPage
I've made the changes to the models so that this structure is used for creating new content and fixed the relevant views to point to the ChildPage directly. But now I want to migrate the current data to the new structure and I'm not sure how to go about it... Ideally this would be done in a migration so that we would not have to do any of this manipulation by hand.
Is there a way to move these ChildPage's up the tree programmatically during a migration?
Unfortunately there's a hard limitation that (probably) rules out the possibility of doing page tree adjustments within migrations: tree operations such as inserting, moving and deleting pages are implemented as methods on the Page model, and within a migration you only have access to a 'dummy' version of that model, which only gives you access to the database fields and basic ORM methods, not those custom methods.
(You might be able to work around this by putting from wagtail.wagtailcore.models import Page in your migration and using that instead of the standard Page = apps.get_model("wagtailcore", "Page") approach, but I wouldn't recommend that - it's liable to break if the migration is run at a point in the migration sequence where the Page model is still being built up and doesn't match the 'real' state of the model.)
Instead, I'd suggest writing a Django management command to do the tree manipulation - within a management command it is safe to import the Page model from wagtailcore, as well as your specific page models. Page provides a method move(target, pos) which works as per the Treebeard API - the code for moving your child pages might look something like:
from myapp.models import IndexPage
# ...
for index_page in IndexPage.objects.all():
for child_page in index_page.get_children():
child_page.move(index_page, 'right')
index_page.delete()
Theoretically it should be possible to build a move() using the same sort of manipulations that Daniele Miele demonstrates in Django-treebeard and Wagtail page creation. It'd look something like this Python pseudocode:
def move(page, target):
# assuming pos='last_child' but other cases follow similarly,
# just with more bookkeeping
# first, cut it out of its old tree
page.parent.numchild -= 1
for sib in page.right_siblings: # i.e. those with a greater path
old = sib.path
new = sib.path[:-4] + (int(sib.path[-4:])-1):04
sib.path = new
for nib in sib.descendants:
nib.path = nib.path.replace_prefix(old, new)
# now, update itself
old_path = page.path
new_path = target.path + (target.numchild+1):04
page.path = new_path
old_url_path = page.url_path
new_url_path = target.url_path + page.url_path.last
page.url_path = new_url_path
old_depth = page.depth
new_depth = target.depth + 1
page.depth = new_depth
# and its descendants
depth_change = new_depth - old_depth
for descendant in page.descendants:
descendant.path = descendant.path.replace_prefix(old_path, new_path)
descendant.url_path = descendant.url_path.replace_prefix(old_path, new_path)
descendant.depth += depth_change
# finally, update its new parent
target.numchild += 1
The core concept that makes this manipulation simpler than it looks is: when a node gets reordered or moved, all its descendants need to be updated, but the only update they need is the exact same update their ancestor got. It's applied as a prefix replacement (if str) or a difference (if int), neither of which requires knowing anything about the descendant's exact value.
That said, I haven't tested it; it's complex enough to be easy to mess up; and there's no way of knowing if I updated every invariant that Wagtail cares about. So there's something to be said for the management command way as well.

Replacing the duplicate function for the 'unique' plugin

I am trying to write my own $.jstree.defaults.unique.duplicate function and replace it.
I tried doing so in the following jsFiddle (line: 68):
$.jstree.defaults.unique.duplicate = function(name, counter){
return name + "_test";
};
Steps to test:
Select Devices
Create Node called Node1
Create Another node under Devices called Node1
It will use the jstree default method to replace it with the default node name vs what my function provides
http://jsfiddle.net/2mbq86at/
Am I doing something wrong? Thanks in advance.
In your code you specify text to use in course of creating a new node.
Take out the pre-defined text and things will start to work as desired for your function $.jstree.defaults.unique.duplicate !
self.createFileNode = function (data) {
//Below code only allows files to be created within folders.
//Structure it as per createFolder method to create files at root
var data = {
//'id': 'tempId',
//'text': 'iOS 8'
}
....
In that case of course a new node's text will be the default 'New node'.
The best is to set a unique node name right upfront on creating a new node, like "text" : "OS_" + (new Date).getTime().
Still for the case of renaming nodes and duplicates: using the unique plugin, on renaming and choosing a duplicate text the node's text will fall back to the original one. If that is not what is desired, things get more complicated as the rename_node.jstree event will not be fired in that case.

GXT 3 dynamically update an element in a treeStore

i'm currently using GXT 3 to display elements in a Tree.
These elements are retrieved from database and identified in the Tree by their id (by that, I mean that the id is the ModelKeyProvider of my store).
I also made it possible for users to create objects locally in the tree with the following code:
private Tree<EntityDAO, String> tree;
private TreeStore<EntityDAO> store;
int count = 1;
// instanciation and irrelevant stuff
...
EntityDAO sel = tree.getSelectionModel().getSelectedItem();
EntityDAO child = new EntityDAO();
child.setId((long) count);
store.add(store.getParent(sel), child);
count++;
tree.setExpanded(sel, true);
tree.getSelectionModel().select(child, false);
As you can see, i set a temporary id (count) to my local object.
The issue occurs when I save my object in database. A permanent id is then set to my EntityDAO but when i try to set this id to my local object to sync it with the database, it doesn't work.
I've tried to modify the child id directly
child.setId(result);
tree.update(child);
I've tried to add a copy of my object with the proper id, and then to remove my object from the tree
EntityDAO newPR = child;
newPR.setId(result);
store.add(store.getParent(child), newPR);
store.remove(child);
But the display is never updated. Any clue?
Let's discuss about the first way you tried, the update method:
child.setId(result);
tree.update(child);
From the update method API state this :
Replaces the item that matches the key of the given item, and fires a
StoreUpdateEvent to indicate that this change has occurred. Any
changes to the previous model via it's record instance will be lost
and the record will be removed. This will not cause the sort or filter
to be re-applied to the object. Overrides: update(...) in Store
Parameters: item the new item to take its place in the Store.
So basically, the update method will replace the item inside the store that have the same key with your parameter. Your data have a new key that doesn't exist inside the store, that's why it doesn't effected anything to your tree display.
Second, let's discuss the create a copy of your object and set it with the proper id:
EntityDAO newPR = child;
newPR.setId(result);
store.add(store.getParent(child), newPR);
store.remove(child);
This way actually will work, but you only have one small problem. The first line of your code actually just give you a variable that have a reference to your old object (the child object), so whenever you remove the child, the newPR also removed. You should really create a new object using the constructor, here how I think you should do it:
EntityDAO newPR = new EntityDAO();
newPR.setId(result);
newPR.setOtherProperty(child.getOtherProperty());
// just copy all property of child to newPR
store.add(store.getParent(child), newPR);
store.remove(child);
Hope this can help you.

CQ - Moving content from one page to another

I realize that this is a pretty specific question but I would imagine someone has run into this before. So I've got about fifty pages or so that were created about a year ago. We're trying to revamp the page with new components specifically in the header and the footer. Except the content in the main-content area will stay the same. So I'm trying to move over everything from the old pages to the new pages but just keep the main-content area. The problem is I can't just change the resource type on the old page to point to the new page components because the content is different and I'll have a bunch of nodes in the header and footer that I don't want. For example here is my current content structure:
Old Content
star-trek
jcr:content
header
nav
social
chat
main-content
column-one
column-two
footer
sign-up
mega-menu
New Content
star-wars
jcr:content
masthead
mega-menu
main-content
column-one
column-two
bottom-footer
left-links
right-links
Does anybody have any ideas on how to move just the content in the main-content node and somehow remove the other nodes. I'm trying to somehow do this programmatically cause I don't want to create 50 pages from scratch. Any help is appreciated!
You can use the JCR API to move things around at will, I would
Block users from accessing the content in question. Can be done with temporary ACLs, or by closing access on the front-end if you can.
Run a script or servlet that changes the content using JCR APIs
Check the results
Let users access the content again
For the content modification script I suggest a script that modifies a single page (i.e. you call it with an HTTP request that ends in /content/star-trek.modify.txt) so that you can run it either on a single page, for testing, or on a group of pages once it's good.
The script starts form the current node, recurses into it to find nodes that it knowns how to modify (based on their sling:resourceType), modifies them and reports what it did in the logs or on its output.
To modify nodes the script uses the JCR Node APIs to move things around (and maybe Worskpace.move).
It is indeed possible to write a code which does what you need :
package com.test;
import java.io.File;
import java.io.IOException;
import javax.jcr.ItemExistsException;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.Node;
import org.apache.jackrabbit.commons.JcrUtils;
import org.apache.jackrabbit.core.TransientRepository;
import org.xml.sax.SAXException;
public class test {
public void test(Document doc) throws RepositoryException {
try {
// Create a connection to the CQ repository running on local host
Repository repository = JcrUtils
.getRepository("http://localhost:4502/crx/server");
System.out.println("rep is created");
// Create a Session
javax.jcr.Session session = repository.login(new SimpleCredentials(
"admin", "admin".toCharArray()));
System.out.println("session is created");
String starTrekNodePath = "/content/path/";
String starWarsNodePath = "/content/anotherPath"
Node starTrekpageJcrNode = null;
Node starWarstext = null;
setProperty(java.lang.String name, Node value)
boolean starTrekNodeFlag = session.nodeExists(starTrekNodePath);
boolean starWarsNodeFlag = session.nodeExists(starWarsNodePath);
if (starTrekNodeFlag && starWarsNodeFlag) {
System.out.println("to infinity and beyond");
Node starTrekNode = session.getNode(starTrekNodePath);
Node starWarsNodeFlag = session.getNode(starWarsNodePath);
//apply nested looping logic here; to loop through all pages under this node
//assumption is that you have similar page titles or something
//on these lines to determine target and destination nodes
//2nd assumption is that destination pages exist with the component structures in q
//once we have the target nodes, the following segment should be all you need
Node starTrekChildNode = iterator.next();//assuming you use some form of iterator for looping logic
Node starWarsChildNode = iterator1.next();//assuming you use some form of iterator for looping logic
//next we get the jcr:content node of the target and child nodes
Node starTrekChildJcrNode = starTrekChildNode.getNode("jcr:content");
Node starWarsChildJcrNode = starWarsChildNode.getNode("jcr:content");
// now we fetch the main-component nodes.
Node starTrekChildMCNode = starTrekChildJcrNode.getNode("main-content");
Node starWarsChildMCNode = starWarsChildJcrNode.getNode("main-content");
//now we fetch each component node
Node starTrekChildC1Node = starTrekChildMCNode.getNode("column-one");
Node starTrekChildC2Node = starTrekChildMCNode.getNode("column-two");
Node starWarsChildC1Node = starWarsChildMCNode.getNode("column-one");
Node starWarsChildC2Node = starWarsChildMCNode.getNode("column-two");
// fetch the properties for each node of column-one and column-two from target
String prop1;
String prop2;
PropertyIterator iterator = starTrekChildC1Node.getProperties(propName);
while (iterator.hasNext()) {
Property prop = iterator.nextProperty();
prop1 = prop.getString();
}
PropertyIterator iterator = starTrekChildC2Node.getProperties(propName);
while (iterator.hasNext()) {
Property prop = iterator.nextProperty();
prop2 = prop.getString();
}
// and now we set the values
starWarsChildC1Node.setProperty("property-name",prop1);
starWarsChildC2Node.setProperty("property-name",prop2);
//end loops as appropriate
}
Hopefully this should set you on the right track. You'd have to figure out how you want to identify destination and target pages, based on your folder structure in /content, but the essential logic should be the same
The problem with the results I'm seeing here is that you are writing servlets that execute JCR operations to move things around. While technically that works, it's not really a scalable or reusable way to do this. You have to write some very specific code, deploy it, execute it, then delete it (or it lives out there forever). It's kind of impractical and not totally RESTful.
Here are two better options:
One of my colleagues wrote the CQ Groovy Console, which gives you the ability to use Groovy to script changes to the repository. We frequently use it for content transformations, like you've described. The advantage to using Groovy is that it's script (not compiled/deployed code). You still have access to the JCR API if you need it, but the console has a bunch of helper methods to make things even easier. I highly recommend this approach.
https://github.com/Citytechinc/cq-groovy-console
The other approach is to use the Bulk Editor tool in AEM. You can export a TSV of content, make changes, then reimport. You'll have to turn the import feature on using an administrative function, but I've used this with some success. Beware, it's a bit buggy though, with array value properties.

Check for Valid Image using getFilesAsync()

Using WinJS, while looping through a directory, how to retrieve only images in that particular directory and ignoring any other file extension, including the DoubleDots .. and the SingleDot . etc?
Something like:
var dir = Windows.Storage.KnownFolders.picturesLibrary;
dir.getFilesAsync().done(function (filesFound) {
for(var i=0; i < filesFound.length; i++){}
if(filesFound[i] IS_REALLY_AN_IMAGE_(jpeg,jpg,png,gif Only)){
//Retrieve it now!
}else{
//Escape it.
}
}})
Instead of trying to process pathnames, it will work much better to use a file query, which lets the file system do the search/filtering for you. A query also allows you to listen for the query's contentschanged event if you want to dynamically track the folder contents rather than explicitly enumerating again.
A query is created via StorageFolder.createFileQuery, createFolderQuery, or other variants. In your particular case, where you want to filter by file types, you can use createFileQueryWithOptions. This function takes a QueryOptions object which you can initialize with an array of file types. For example:
var picturesLibrary = Windows.Storage.KnownFolders.picturesLibrary;
var options = new Windows.Storage.Search.QueryOptions(
Windows.Storage.Search.CommonFileQuery.orderByName, [".jpg", ".jpeg", ".png", ".gif"]);
//Could also use orderByDate instead of orderByName
if (picturesLibrary.areQueryOptionsSupported(options)) {
var query = picturesLibrary.createFileQueryWithOptions(options);
showResults(query.getFilesAsync());
}
where showResults is some function that takes the promise from query.getFilesAsync and iterates as needed.
I go into this subject at length in Chapter 11 of my free ebook, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, in the section "Folders and Folder Queries". Also refer to the Programmatic file search sample, as I do in the book.
When you want to display the image files, be sure to use thumbnails instead of loading the whole image (images are typically much larger than a display). That is, for each StorageFile, call its getThumbnailAsync or getScaledImageAsThumbnailAsync method. Pass the resulting thumbnail (blob) to URL.createObjectURL which returns a URL you can assign to an img.src attribute. Or you can use a WinJS.UI.ListView control, but that's another topic altogether (see Chapter 7 of my book).