问:
I'm having some trouble deleting some commits from Git
Currently my repo is as pictured:

I need to delete the top 3 commits as they are the product of faulty automated processes. I want to really delete those commits so that I can get rid of their artifacts cluttering the repo. One of the commits made 2000+ incorrect modifications to code, and I want to delete those 3 commits and GC their artifacts.

To that end, I ran these commands:

```<code>git reset --hard 3e2fb0bcd1471d33c940a4da3809ce6c48dd1c32
git push -f origin master
</code>```

And that got me this far:

Which is as I expected. However, I have two problems:

1) those commits are still there. They are not part of the codebase's history; they are a mistake and I don't want to keep them in the repo. How do I erase them completely from the repo?

2) when I run

```<code>git pull origin master
</code>```

the repo reverts to the state depicted in image 1. I though I had synchronized both the local and the remote repos! Why is this happening? How do I ensure that commit 3e2fb0... is the 'latest' commit?

答:
[git reset --hard <hash> and git push -f origin master succeeded, but]

1) those commits are still there.

Yes.  Those commits are permanent.  Those three hash IDs represent those three commits, and vice versa.  What one does in Git is to take away your own name for those commits (git reset which changes your master so that it does not name one of those three hash IDs), and then take away someone else's name as well (git push -f origin master), so that their master does not name one of those hash IDs.

(I don't know the hash ID or I would use that here.  A hash ID is the "true name" of the commit; names like master are just symbols that allow us, or Git, to find one specific commit, whose parent(s) find more commit(s), whose parents find yet more commits, and so on.  This is the "reachability" idea: a commit that is reachable from a name by this parent-following process—note that any name will do—is "alive" in the repository.  A commit that is not reachable, is not quite alive: it's either in the process of eventually dying, or in the process of being created.)

Your own remote-tracking name, origin/master, retained the old hash ID until you ran git push -f origin master.  When your Git saw that their Git had accepted the name-update, your Git updated your copy of their name.  That took away the second name you had for that hash ID.  If that was all of your names, as it probably was, that suffices for your repository.

If the Git at origin had only one name making those three commits reachable, that one name being master, you have updated that Git / repository so that it no longer has any names for those commits.

The real question at this point is then: what other Git repositories have other names for one of those three hash IDs?

... they are a mistake and I don't want to keep them in the repo. How do I erase them completely from the repo?

In general, you don't: it's normally a futile effort.  A commit that is unreachable from any name—branch or tag name, or other Git internal name—will eventually expire and be "garbage collected".  This is what makes the commit truly go away.  Until then, though, anyone with the hash ID can look up the commit by hash ID.

If no other Git has picked up those three hash IDs, then you have removed the reachability of those three hash IDs from both affected repositories, which is all you really need.

If anyone else does have the three commits, that other Git can offer them (by hash ID and a suggested name) to your Git and/or to the Git that you call origin.  Your Git and origin's Git, being Gits, will very likely say, in Borg fashion, "your technological distinctiveness will be added to my repository" and take them in again.  It's very difficult to be rid of a commit forever, because once it is made, Git really doesn't want to be rid of it.

2) when I run

```<code>git pull origin master
</code>```

the repo reverts to the state depicted in image 1. I though I had synchronized both the local and the remote repos! Why is this happening? How do I ensure that commit 3e2fb0... is the 'latest' commit?

If your own git push -f origin master succeeded, but a later git pull origin master brought the three commits back, that means that the Git over on origin decided that its master should point to the tip-most of those three commits.

What would make that happen?  Well, suppose there's a third Git that has those three commits, and probably a name for one of them.  The user of that third Git runs git push <url> <name>:master or even git push <url> <hash-id>:master.  That makes their (third) Git call up the Git at <url>, which is the Git you call origin, and offer to it that particular commit by its hash ID.

The Git at origin takes that hash ID (and re-obtains the commit itself if necessary, if it has managed to garbage-collect the copy it got from you) and looks at its parent IDs, and the parents' parent IDs, and so on, and sees whether it can add that commit's technological distinctiveness to its repository.  Sure enough, it can: those three commits easily slot right in atop its own master!  So the Git at origin takes those commits, adding them to its repository, and updates its master to point to the tip-most one.

This is what makes it so hard to get rid of these things.  A commit, once made, easily fits into every clone of a repository, since it matches up with at least one clone.  Every Git that calls up another Git tends to get a copy of its latest commits, which spread like viruses through the technologically-grabby Borg-ness of Git.

To really, truly, get rid of a commit, you have to make it unreachable in your repository and unreachable in every other repository that has it so that none of these Gits offers it back to any other Git at any point.  Otherwise, like some stubborn virus, it just keeps coming back.

Things you can do if you have a controlled central repository

There are a few things you can do if everyone agrees that some central repository is the "source of truth".  For instance, suppose the Git you call origin is on your own master server, or even on GitHub or Bitbucket or some other distribution point, and you have sufficient control of that server/Git.  You can arrange for that Git to reject the bad commit by its hash ID, or by some other trickery.

The "refuse by hash ID" is the simplest.  You can, for instance, create or adjust a pre-receive or update hook on the server at origin for that particular Git repository.  Have the repository check whether the incoming push operation would make any of the undesired hash IDs become reachable.  If so, reject the push.  Use a message that the person git pushing can read and understand: "commit XXXXX is a mistake, please throw it out of your own Git using git reset; contact us at <url> in case of confusion".

Slightly more complicated and error-prone, you can reset your own master to make the unwanted commits unreachable, then make a new commit atop it.  Then git push -f the result to origin.  Now if some third Git user runs git push origin to send the original three commits, they won't slot easily in.  That user would also have to force-push.

The problem you will run into is that some naive user will think: Oh, I should rebase to make my code slot in more easily!  The git rebase command copies old commits that, e.g., don't slot right in, so that they become new commits with new hash IDs that do slot right in.  This naive user will think that the three bad-virus commits are good, copy them to slightly-altered versions that resist your antibodies, and re-inject them into the origin repository.

This can still happen even with the "reject by hash ID" technique, but at least there you have the strongly worded message that all your users will read and understand.  (Cough)

The most complicated method, which is proof against both original hash IDs and rebase-copied versions, is to write a pre-receive or update hook that recognizes the bad commits by something unique that makes them bad: not just the hash ID itself, but something that stands out about the content associated with the commit.  That is, one treats the bad commits like a mutating virus, finds an appropriate signature that indicates that a commit contains the to-be-refused virus, and refuses it.

Of course, all of these methods require that you have sufficient control of the Git at origin.

Body:
Currently my repo is as pictured:

I need to delete the top 3 commits as they are the product of faulty automated processes. I want to really delete those commits so that I can get rid of their artifacts cluttering the repo. One of the commits made 2000+ incorrect modifications to code, and I want to delete those 3 commits and GC their artifacts.

To that end, I ran these commands:

git reset --hard 3e2fb0bcd1471d33c940a4da3809ce6c48dd1c32
git push -f origin master

And that got me this far:

Which is as I expected. However, I have two problems:

1) those commits are still there. They are not part of the codebase's history; they are a mistake and I don't want to keep them in the repo. How do I erase them completely from the repo?

2) when I run

git pull origin master

the repo reverts to the state depicted in image 1. I though I had synchronized both the local and the remote repos! Why is this happening? How do I ensure that commit 3e2fb0... is the 'latest' commit?

Body:
[git reset --hard <hash> and git push -f origin master succeeded, but]

1) those commits are still there.

Yes.  Those commits are permanent.  Those three hash IDs represent those three commits, and vice versa.  What one does in Git is to take away your own name for those commits (git reset which changes your master so that it does not name one of those three hash IDs), and then take away someone else's name as well (git push -f origin master), so that their master does not name one of those hash IDs.

(I don't know the hash ID or I would use that here.  A hash ID is the "true name" of the commit; names like master are just symbols that allow us, or Git, to find one specific commit, whose parent(s) find more commit(s), whose parents find yet more commits, and so on.  This is the "reachability" idea: a commit that is reachable from a name by this parent-following process—note that any name will do—is "alive" in the repository.  A commit that is not reachable, is not quite alive: it's either in the process of eventually dying, or in the process of being created.)

Your own remote-tracking name, origin/master, retained the old hash ID until you ran git push -f origin master.  When your Git saw that their Git had accepted the name-update, your Git updated your copy of their name.  That took away the second name you had for that hash ID.  If that was all of your names, as it probably was, that suffices for your repository.

If the Git at origin had only one name making those three commits reachable, that one name being master, you have updated that Git / repository so that it no longer has any names for those commits.

The real question at this point is then: what other Git repositories have other names for one of those three hash IDs?

... they are a mistake and I don't want to keep them in the repo. How do I erase them completely from the repo?

In general, you don't: it's normally a futile effort.  A commit that is unreachable from any name—branch or tag name, or other Git internal name—will eventually expire and be "garbage collected".  This is what makes the commit truly go away.  Until then, though, anyone with the hash ID can look up the commit by hash ID.

If no other Git has picked up those three hash IDs, then you have removed the reachability of those three hash IDs from both affected repositories, which is all you really need.

If anyone else does have the three commits, that other Git can offer them (by hash ID and a suggested name) to your Git and/or to the Git that you call origin.  Your Git and origin's Git, being Gits, will very likely say, in Borg fashion, "your technological distinctiveness will be added to my repository" and take them in again.  It's very difficult to be rid of a commit forever, because once it is made, Git really doesn't want to be rid of it.

2) when I run

git pull origin master

the repo reverts to the state depicted in image 1. I though I had synchronized both the local and the remote repos! Why is this happening? How do I ensure that commit 3e2fb0... is the 'latest' commit?

If your own git push -f origin master succeeded, but a later git pull origin master brought the three commits back, that means that the Git over on origin decided that its master should point to the tip-most of those three commits.

What would make that happen?  Well, suppose there's a third Git that has those three commits, and probably a name for one of them.  The user of that third Git runs git push <url> <name>:master or even git push <url> <hash-id>:master.  That makes their (third) Git call up the Git at <url>, which is the Git you call origin, and offer to it that particular commit by its hash ID.

The Git at origin takes that hash ID (and re-obtains the commit itself if necessary, if it has managed to garbage-collect the copy it got from you) and looks at its parent IDs, and the parents' parent IDs, and so on, and sees whether it can add that commit's technological distinctiveness to its repository.  Sure enough, it can: those three commits easily slot right in atop its own master!  So the Git at origin takes those commits, adding them to its repository, and updates its master to point to the tip-most one.

This is what makes it so hard to get rid of these things.  A commit, once made, easily fits into every clone of a repository, since it matches up with at least one clone.  Every Git that calls up another Git tends to get a copy of its latest commits, which spread like viruses through the technologically-grabby Borg-ness of Git.

To really, truly, get rid of a commit, you have to make it unreachable in your repository and unreachable in every other repository that has it so that none of these Gits offers it back to any other Git at any point.  Otherwise, like some stubborn virus, it just keeps coming back.

Things you can do if you have a controlled central repository

There are a few things you can do if everyone agrees that some central repository is the "source of truth".  For instance, suppose the Git you call origin is on your own master server, or even on GitHub or Bitbucket or some other distribution point, and you have sufficient control of that server/Git.  You can arrange for that Git to reject the bad commit by its hash ID, or by some other trickery.

The "refuse by hash ID" is the simplest.  You can, for instance, create or adjust a pre-receive or update hook on the server at origin for that particular Git repository.  Have the repository check whether the incoming push operation would make any of the undesired hash IDs become reachable.  If so, reject the push.  Use a message that the person git pushing can read and understand: "commit XXXXX is a mistake, please throw it out of your own Git using git reset; contact us at <url> in case of confusion".

Slightly more complicated and error-prone, you can reset your own master to make the unwanted commits unreachable, then make a new commit atop it.  Then git push -f the result to origin.  Now if some third Git user runs git push origin to send the original three commits, they won't slot easily in.  That user would also have to force-push.

The problem you will run into is that some naive user will think: Oh, I should rebase to make my code slot in more easily!  The git rebase command copies old commits that, e.g., don't slot right in, so that they become new commits with new hash IDs that do slot right in.  This naive user will think that the three bad-virus commits are good, copy them to slightly-altered versions that resist your antibodies, and re-inject them into the origin repository.

This can still happen even with the "reject by hash ID" technique, but at least there you have the strongly worded message that all your users will read and understand.  (Cough)

The most complicated method, which is proof against both original hash IDs and rebase-copied versions, is to write a pre-receive or update hook that recognizes the bad commits by something unique that makes them bad: not just the hash ID itself, but something that stands out about the content associated with the commit.  That is, one treats the bad commits like a mutating virus, finds an appropriate signature that indicates that a commit contains the to-be-refused virus, and refuses it.

Of course, all of these methods require that you have sufficient control of the Git at origin.


来源: stackexchange

问:
Passing date to controller date time adds random time stamp
I am new to Angular and trying to pass date from the angular component to MVC API(C#) date time. Looks like I am not getting default time stamp 12:00 am in API method. Let me know what I am doing wrong.

Client Model:

```<code>export class TestClass {
    constructor(
        public ctyName: string = '',
        public stateName: string = '',
        public zipCode: string = '',
        public country: string = '',
        public effectiveDate: Date = null

    ) {
    }
}
</code>```

Service Model

```<code>public class TestClass
{
    public string CityName { get; set; }
    public string StateName { get; set; }
    public string ZipCode { get; set; }
    public string Country { get; set; }
    public DateTime EffectiveDate: { get; set; }
}
</code>```

Client Method - Assignment

```<code>var month = '1';
var day = '1';
var year = '2016';
effectiveDate = new Date(year, month - 1, day);
</code>```

Api Method

```<code>[HttpPost("[action]/{id}")]
public PostResult UpdateData(string id, [FromBody]TestClass testClass)
{
}
</code>```

Value I am receiving in API

2016-01-01 05:00:00.000

Thanks for your time

答:
Please try to change this line:

```<code>effectiveDate = new Date(year, month - 1, day);
</code>```

to this:

```<code>effectiveDate = new Date(year, month - 1, day, 0, 0, 0, 0);
</code>```

This should help you.

The case is the signature of JS Date constructor is:

```<code>new Date(year, month, day, hours, minutes, seconds, milliseconds);
</code>```

In your case you only specifying year, month and day. But hours, minutes and seconds are filled automatically by your current local time.

Check this for more reference:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date

ContentLicense: CC BY-SA 3.0

Body:
I am new to Angular and trying to pass date from the angular component to MVC API(C#) date time. Looks like I am not getting default time stamp 12:00 am in API method. Let me know what I am doing wrong.

Client Model:

export class TestClass {
    constructor(
        public ctyName: string = '',
        public stateName: string = '',
        public zipCode: string = '',
        public country: string = '',
        public effectiveDate: Date = null

    ) {
    }
}

Service Model

public class TestClass
{
    public string CityName { get; set; }
    public string StateName { get; set; }
    public string ZipCode { get; set; }
    public string Country { get; set; }
    public DateTime EffectiveDate: { get; set; }
}

Client Method - Assignment

var month = '1';
var day = '1';
var year = '2016';
effectiveDate = new Date(year, month - 1, day);

Api Method

[HttpPost("[action]/{id}")]
public PostResult UpdateData(string id, [FromBody]TestClass testClass)
{
}

Value I am receiving in API

2016-01-01 05:00:00.000

Thanks for your time

Body:
Please try to change this line:

effectiveDate = new Date(year, month - 1, day);

to this:

effectiveDate = new Date(year, month - 1, day, 0, 0, 0, 0);

This should help you.

The case is the signature of JS Date constructor is:

new Date(year, month, day, hours, minutes, seconds, milliseconds);

In your case you only specifying year, month and day. But hours, minutes and seconds are filled automatically by your current local time.

Check this for more reference:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date


问:
Rest vs Soap consuming
Can anyone help me understanding difference between consuming rest service is different from consuming soap service code wise ?

I need core difference in consuming them

答:
Consuming Soap means you need to pass to the consuming app the wsdl file that contains the definition of the objects and methods in your web service as a web reference. Each time you update anything object class or method in your web service, you need to update the reference in your consuming app.

In order to consume rest service you just need to know the endpoint, the url that the service resides, and any parameters and headers that the web service requires you to have before returning any data back. Once you know that you can just call a httpclient instance and create a request to get or post to that endpoint.

Title: Rest vs Soap consuming


问:
debugging caret with SMOTE in R
I'm trying to use SMOTE in R within the trainControl function in caret. Following the author's example I do as follows:

```<code>#first, create an imbalanced data set
set.seed(2969)
imbal_train <- twoClassSim(10000, intercept = -20, linearVars = 20)
imbal_test  <- twoClassSim(10000, intercept = -20, linearVars = 20)

table(imbal_train$Class)
Class1 Class2
 9411    589
</code>```

I want to use the SMOTE algorithm to oversample my minority class. However, this has to be done carefully. For instance, we shouldn't oversample before doing cross validation. This would lead us to optimistic generalization error.

```<code>#create my folds (5 in this case)
folds <- createFolds(factor(imbal_train$Class), k = 5, list = TRUE,returnTrain=TRUE)

#trainControl to set up my training phase.
ctrl <- trainControl(method = "cv", index = folds,
                 classProbs = TRUE,
                 summaryFunction = twoClassSummary,
                 savePredictions = "all",
                 ## new option here:
                 sampling = "smote")

#train the model
set.seed(5627)
smote_inside <- train(Class ~ ., data = imbal_train,
                  method = "treebag",
                  nbagg = 50,
                  metric = "ROC",
                  trControl = ctrl)
</code>```

It runs without error. I now want to see the training and testing set used in each iteration. I need to make sure that before oversampling the training folders, one folder was hold out and no new synthetic records were created inside of it.

Looking into the objects output by train, I could see that smote_inside$control may have some information. Concretely, it has the index and  index_out: these are the row indexes for the training and testing in each cv iteration. However, when I do :

```<code>lista=smote_inside$control
dd=imbal_train[lista$index$Fold1,] #training data first cv iteration
table(dd$Class)
Class1 Class2
7529    471
</code>```

You can see that it is still imbalanced. SMOTE is supposed to create some synthetic records from the minority class. Maybe this information is saved in another place?

Questions:

How can I see the new training records that were created using smote to balance the data?

How can I be sure that the testing folder wasn't contaminated with the oversampling?

Where can I find what caret is doing with SMOTE? pointers to a source code.

答:
Some answers:

It does not retain that information

It is designed not to contaminate the holdout data. If you want proof (beyond what is shown in the link that you reference), look at createModel to see how it does the sampling and predictionFunction for how the data are handled prior to prediction.

The package sources are available basically everywhere. The two functions above (along with probFunction) to the work.

Title: debugging caret with SMOTE in R

Body:
I'm trying to use SMOTE in R within the trainControl function in caret. Following the author's example I do as follows:

#first, create an imbalanced data set
set.seed(2969)
imbal_train <- twoClassSim(10000, intercept = -20, linearVars = 20)
imbal_test  <- twoClassSim(10000, intercept = -20, linearVars = 20)

table(imbal_train$Class)
Class1 Class2
 9411    589

I want to use the SMOTE algorithm to oversample my minority class. However, this has to be done carefully. For instance, we shouldn't oversample before doing cross validation. This would lead us to optimistic generalization error.

#create my folds (5 in this case)
folds <- createFolds(factor(imbal_train$Class), k = 5, list = TRUE,returnTrain=TRUE)

#trainControl to set up my training phase.
ctrl <- trainControl(method = "cv", index = folds,
                 classProbs = TRUE,
                 summaryFunction = twoClassSummary,
                 savePredictions = "all",
                 ## new option here:
                 sampling = "smote")

#train the model
set.seed(5627)
smote_inside <- train(Class ~ ., data = imbal_train,
                  method = "treebag",
                  nbagg = 50,
                  metric = "ROC",
                  trControl = ctrl)

It runs without error. I now want to see the training and testing set used in each iteration. I need to make sure that before oversampling the training folders, one folder was hold out and no new synthetic records were created inside of it.

Looking into the objects output by train, I could see that smote_inside$control may have some information. Concretely, it has the index and  index_out: these are the row indexes for the training and testing in each cv iteration. However, when I do :

lista=smote_inside$control
dd=imbal_train[lista$index$Fold1,] #training data first cv iteration
table(dd$Class)
Class1 Class2
7529    471

You can see that it is still imbalanced. SMOTE is supposed to create some synthetic records from the minority class. Maybe this information is saved in another place?

Questions:

How can I see the new training records that were created using smote to balance the data?

How can I be sure that the testing folder wasn't contaminated with the oversampling?

Where can I find what caret is doing with SMOTE? pointers to a source code.


问:
Why is data still remaining after I remove an item from a ListView and a corresponding ObservableList?
I'm creating a ListView using javafx and allowing the user to Add, Check Out, Check In, and Delete elements of the ListView. When an element is deleted, though, and another element is put into its place, the program acts as if the deleted element is still present therefor if I try to check out the newly added element, it instead checks out the one I just tried to delete.

Simply put, this is what happen:

Add --> "item 1"

Add --> "item 2"

Delete --> "item 1" (item 1 is now removed and item 2 shifts up in the ViewList)

 Check out --> "item 2" (a prompt appears allowing the user to state who the item is checked out to and the date it was checked out)

 After the user fills in the information, the line now reads "item 1 was checked out to "name" on "date"."

It should say item 2 instead of item 1 and I can't figure out why it is doing so.

LibraryGUI.java

```<code>import java.util.ArrayList;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.ChoiceBoxListCell;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class LibraryGUI extends Application implements EventHandler<ActionEvent> {
    private Library lib = new Library();
    private ObservableList<String> item = FXCollections.observableArrayList();
    private ListView<String> items = new ListView<String>(item);
    private ArrayList<String> titles = new ArrayList<String>();
    private ArrayList<String> formats = new ArrayList<String>();
    private String title, format, loanedTo, dateLoaned;
    private Button add, checkOut, checkIn, delete, accept1, accept2, accept3, accept4, cancel1, cancel2, cancel3,
            cancel4;
    private TextField tf1, tf2, tf3, tf4;
    private Stage stage1, stage2, stage3, stage4;
    private int itemNum;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        LibraryGUI gui = new LibraryGUI();
        primaryStage.setTitle("Personal Lending Library");
        items.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
        Scene scene = new Scene(gui.layout(), 325, 340);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    public Pane layout() {

        add = new Button("Add");
        add.setOnAction(this);
        add.setLayoutX(5);
        add.setLayoutY(310);
        add.setPrefWidth(75);

        checkOut = new Button("Check Out");
        checkOut.setOnAction(this);
        checkOut.setLayoutX(85);
        checkOut.setLayoutY(310);
        checkOut.setPrefWidth(75);

        checkIn = new Button("Check In");
        checkIn.setOnAction(this);
        checkIn.setLayoutX(165);
        checkIn.setLayoutY(310);
        checkIn.setPrefWidth(75);

        delete = new Button("Delete");
        delete.setOnAction(this);
        delete.setLayoutX(245);
        delete.setLayoutY(310);
        delete.setPrefWidth(75);

        Pane layout = new Pane();
        items.setItems(item);
        items.setCellFactory(ChoiceBoxListCell.forListView(item));
        items.setLayoutX(5);
        items.setLayoutY(5);
        items.setPrefWidth(315);
        items.setPrefHeight(300);
        layout.getChildren().addAll(items, add, checkOut, checkIn, delete);
        return layout;
    }

    @Override
    public void handle(ActionEvent event) {
        if (event.getSource() == add) {

            accept1 = new Button("Accept");
            accept1.setPrefWidth(70);
            accept1.setLayoutX(125);
            accept1.setLayoutY(65);
            accept1.setOnAction(this);
            cancel1 = new Button("Cancel");
            cancel1.setPrefWidth(70);
            cancel1.setLayoutX(205);
            cancel1.setLayoutY(65);
            cancel1.setOnAction(this);

            Text dialog = new Text("Media Item Name:");
            dialog.setLayoutX(20);
            dialog.setLayoutY(45);

            tf1 = new TextField();
            tf1.setPrefHeight(20);
            tf1.setPrefWidth(150);
            tf1.setLayoutX(125);
            tf1.setLayoutY(30);

            Pane layout1 = new Pane();
            layout1.getChildren().addAll(tf1, dialog, accept1, cancel1);

            stage1 = new Stage();
            stage1.setTitle("Item Name");
            stage1.setScene(new Scene(layout1, 400, 100));
            stage1.show();
        }

        if (event.getSource() == cancel1) {
            stage1.close();
        }

        if (event.getSource() == accept1) {
            title = tf1.getText();
            titles.add(tf1.getText());
            stage1.close();

            accept2 = new Button("Accept");
            accept2.setPrefWidth(70);
            accept2.setLayoutX(125);
            accept2.setLayoutY(65);
            accept2.setOnAction(this);
            cancel2 = new Button("Cancel");
            cancel2.setPrefWidth(70);
            cancel2.setLayoutX(205);
            cancel2.setLayoutY(65);
            cancel2.setOnAction(this);

            Text dialog = new Text("Media Item Format:");
            dialog.setLayoutX(15);
            dialog.setLayoutY(45);

            tf2 = new TextField();
            tf2.setPrefHeight(20);
            tf2.setPrefWidth(150);
            tf2.setLayoutX(125);
            tf2.setLayoutY(30);
            format = tf2.getText();

            Pane layout2 = new Pane();
            layout2.getChildren().addAll(tf2, dialog, accept2, cancel2);

            stage2 = new Stage();
            stage2.setTitle("Item Format");
            stage2.setScene(new Scene(layout2, 400, 100));
            stage2.show();
        }

        if (event.getSource() == cancel2) {
            title = null;
            titles.remove(titles.size() - 1);
            stage2.close();
        }

        if (event.getSource() == accept2) {
            format = tf2.getText();
            formats.add(tf2.getText());
            lib.addNewItem(title, format);
            items.getItems().add(title + " (" + format + ")");
            stage2.close();

        }

        if (event.getSource() == checkOut) { //item is being checked out
            item = items.getSelectionModel().getSelectedItems();

            for (int i = 0; i < items.getItems().size(); i++) {
                if (items.getSelectionModel().getSelectedItem()
                        .equals(lib.getItemList().get(i).getTitle() + " (" + lib.getItemList().get(i).getFormat() + ")")

                        && !lib.getItemList().get(i).onLoan) {
                    itemNum = i;

                    accept3 = new Button("Accept");
                    accept3.setPrefWidth(70);
                    accept3.setLayoutX(125);
                    accept3.setLayoutY(65);
                    accept3.setOnAction(this);
                    cancel3 = new Button("Cancel");
                    cancel3.setPrefWidth(70);
                    cancel3.setLayoutX(205);
                    cancel3.setLayoutY(65);
                    cancel3.setOnAction(this);

                    Text dialog = new Text("Loaned To:");
                    dialog.setLayoutX(60);
                    dialog.setLayoutY(45);

                    tf3 = new TextField();
                    tf3.setPrefHeight(20);
                    tf3.setPrefWidth(150);
                    tf3.setLayoutX(125);
                    tf3.setLayoutY(30);

                    Pane layout3 = new Pane();
                    layout3.getChildren().addAll(accept3, cancel3, dialog, tf3);

                    stage3 = new Stage();
                    stage3.setTitle("Check Out");
                    stage3.setScene(new Scene(layout3, 400, 100));
                    stage3.show();

                }
            }
        }

        if (event.getSource() == accept3) {
            loanedTo = tf3.getText();
            lib.getItemList().get(itemNum).loanedTo = loanedTo;
            stage3.close();

            accept4 = new Button("Accept");
            accept4.setPrefWidth(70);
            accept4.setLayoutX(125);
            accept4.setLayoutY(65);
            accept4.setOnAction(this);
            cancel4 = new Button("Cancel");
            cancel4.setPrefWidth(70);
            cancel4.setLayoutX(205);
            cancel4.setLayoutY(65);
            cancel4.setOnAction(this);

            Text dialog = new Text("Date Loaned:");
            dialog.setLayoutX(50);
            dialog.setLayoutY(45);

            tf4 = new TextField();
            tf4.setPrefHeight(20);
            tf4.setPrefWidth(150);
            tf4.setLayoutX(125);
            tf4.setLayoutY(30);

            Pane layout3 = new Pane();
            layout3.getChildren().addAll(accept4, cancel4, dialog, tf4);

            stage4 = new Stage();
            stage4.setTitle("Check Out");
            stage4.setScene(new Scene(layout3, 400, 100));
            stage4.show();

        }

        if (event.getSource() == accept4) {
            stage4.close();
            dateLoaned = tf4.getText();
            lib.getItemList().get(items.getSelectionModel().getSelectedIndex()).dateLoaned = dateLoaned;
            lib.getItemList().get(items.getSelectionModel().getSelectedIndex()).onLoan = true;

            items.getItems().set(items.getSelectionModel().getSelectedIndex(),
                    titles.get(items.getSelectionModel().getSelectedIndex()) + " ("
                            + formats.get(items.getSelectionModel().getSelectedIndex()) + ") loaned to " + loanedTo
                            + " on " + dateLoaned);
        }

        if (event.getSource() == checkIn) {
            for (int i = 0; i < items.getItems().size(); i++) {
                if (items.getSelectionModel().getSelectedItem().contains(lib.getItemList().get(i).getTitle())
                        && items.getSelectionModel().getSelectedItem().contains(lib.getItemList().get(i).getFormat())
                        && lib.getItemList().get(i).onLoan) {
                    itemNum = i;
                    items.getItems().set(items.getSelectionModel().getSelectedIndex(),
                            titles.get(items.getSelectionModel().getSelectedIndex()) + " ("
                                    + formats.get(items.getSelectionModel().getSelectedIndex()) + ")");

                    lib.getItemList().get(items.getSelectionModel().getSelectedIndex()).dateLoaned = null;
                    lib.getItemList().get(items.getSelectionModel().getSelectedIndex()).onLoan = false;

                }
            }
        }

        if (event.getSource() == delete) { //item is being deleted from all lists
            items.getItems().remove(items.getSelectionModel().getSelectedIndex());
            for (int i = 0; i < items.getItems().size(); i++) {
                if (items.getSelectionModel().getSelectedItem().contains(lib.getItemList().get(i).getTitle())
                        && items.getSelectionModel().getSelectedItem().contains(lib.getItemList().get(i).getFormat())) {
                    itemNum = i;
                    lib.getItemList().remove(items.getSelectionModel().getSelectedIndex());
                    items.getItems().remove(items.getSelectionModel().getSelectedIndex());
                    titles.remove(items.getSelectionModel().getSelectedIndex());
                    formats.remove(items.getSelectionModel().getSelectedIndex());

                }
            }
        }

    }

}
</code>```

Library.java

```<code>import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class Library {

    private ObservableList<MediaItem> item = FXCollections.observableArrayList();

    public ObservableList<MediaItem> getItemList() {
        return item;
    }

    void addNewItem(String title, String format) {
        item.add(new MediaItem(title, format));

    }
</code>```

I only recently started programming in Java so my code most likely isn't quite optimized. Any and all help will be greatly appreciated. Thanks.

答:
This isn't a complete answer, since I didn't run your code, but looking at it I had a couple of suggestions for you.

First off, having your entire class be an ActionEvent and having a single 'handle' method is probably not ideal. I would split up that handle method into separate methods, named like this public void handleAdd(ActionEvent event) and add them to your buttons like this add.setOnAction((ActionEvent e) -> handleAdd(e));.
This makes more sense because each button has a specific action associated with it, and that action is contained in a specific method.

Secondly, do you need four lists and a Library object (which looks to be just a wrapper for another list)? You're trying to keep five different lists in sync with each other, and that's bound to cause problems like the ones you're seeing now as well as headache for you or whomever has to read and maintain the code in the future. Consider instead just having your Library and making your ListView use type MediaItem instead of String and add loanedto and dateloaned as properties of MediaItem, assuming that's a class you wrote yourself. You can still show the titles properly in the ListView by setting a cell factory like this:

```<code>items.setCellFactory(new Callback<ListView<MediaItem>, ListCell<MediaItem>>() {
    @Override
    public ListCell<MediaItem> call(ListView<MediaItem> param) {
        return new ListCell<MediaItem>() {
            @Override
            protected void updateItem(MediaItem item, boolean empty) {
                super.updateItem(item, empty);
                if(item != null) {
                    setText(item.getTitle());
                }else{
                    setText("");
                }
            }
        };
    }
});
</code>```

Body:
I'm creating a ListView using javafx and allowing the user to Add, Check Out, Check In, and Delete elements of the ListView. When an element is deleted, though, and another element is put into its place, the program acts as if the deleted element is still present therefor if I try to check out the newly added element, it instead checks out the one I just tried to delete.

Simply put, this is what happen:

Add --> "item 1"

Add --> "item 2"

Delete --> "item 1" (item 1 is now removed and item 2 shifts up in the ViewList)

 Check out --> "item 2" (a prompt appears allowing the user to state who the item is checked out to and the date it was checked out)

 After the user fills in the information, the line now reads "item 1 was checked out to "name" on "date"."

It should say item 2 instead of item 1 and I can't figure out why it is doing so.

LibraryGUI.java

import java.util.ArrayList;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.ChoiceBoxListCell;
import javafx.scene.layout.Pane;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class LibraryGUI extends Application implements EventHandler<ActionEvent> {
    private Library lib = new Library();
    private ObservableList<String> item = FXCollections.observableArrayList();
    private ListView<String> items = new ListView<String>(item);
    private ArrayList<String> titles = new ArrayList<String>();
    private ArrayList<String> formats = new ArrayList<String>();
    private String title, format, loanedTo, dateLoaned;
    private Button add, checkOut, checkIn, delete, accept1, accept2, accept3, accept4, cancel1, cancel2, cancel3,
            cancel4;
    private TextField tf1, tf2, tf3, tf4;
    private Stage stage1, stage2, stage3, stage4;
    private int itemNum;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        LibraryGUI gui = new LibraryGUI();
        primaryStage.setTitle("Personal Lending Library");
        items.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
        Scene scene = new Scene(gui.layout(), 325, 340);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    public Pane layout() {

        add = new Button("Add");
        add.setOnAction(this);
        add.setLayoutX(5);
        add.setLayoutY(310);
        add.setPrefWidth(75);

        checkOut = new Button("Check Out");
        checkOut.setOnAction(this);
        checkOut.setLayoutX(85);
        checkOut.setLayoutY(310);
        checkOut.setPrefWidth(75);

        checkIn = new Button("Check In");
        checkIn.setOnAction(this);
        checkIn.setLayoutX(165);
        checkIn.setLayoutY(310);
        checkIn.setPrefWidth(75);

        delete = new Button("Delete");
        delete.setOnAction(this);
        delete.setLayoutX(245);
        delete.setLayoutY(310);
        delete.setPrefWidth(75);

        Pane layout = new Pane();
        items.setItems(item);
        items.setCellFactory(ChoiceBoxListCell.forListView(item));
        items.setLayoutX(5);
        items.setLayoutY(5);
        items.setPrefWidth(315);
        items.setPrefHeight(300);
        layout.getChildren().addAll(items, add, checkOut, checkIn, delete);
        return layout;
    }

    @Override
    public void handle(ActionEvent event) {
        if (event.getSource() == add) {

            accept1 = new Button("Accept");
            accept1.setPrefWidth(70);
            accept1.setLayoutX(125);
            accept1.setLayoutY(65);
            accept1.setOnAction(this);
            cancel1 = new Button("Cancel");
            cancel1.setPrefWidth(70);
            cancel1.setLayoutX(205);
            cancel1.setLayoutY(65);
            cancel1.setOnAction(this);

            Text dialog = new Text("Media Item Name:");
            dialog.setLayoutX(20);
            dialog.setLayoutY(45);

            tf1 = new TextField();
            tf1.setPrefHeight(20);
            tf1.setPrefWidth(150);
            tf1.setLayoutX(125);
            tf1.setLayoutY(30);

            Pane layout1 = new Pane();
            layout1.getChildren().addAll(tf1, dialog, accept1, cancel1);

            stage1 = new Stage();
            stage1.setTitle("Item Name");
            stage1.setScene(new Scene(layout1, 400, 100));
            stage1.show();
        }

        if (event.getSource() == cancel1) {
            stage1.close();
        }

        if (event.getSource() == accept1) {
            title = tf1.getText();
            titles.add(tf1.getText());
            stage1.close();

            accept2 = new Button("Accept");
            accept2.setPrefWidth(70);
            accept2.setLayoutX(125);
            accept2.setLayoutY(65);
            accept2.setOnAction(this);
            cancel2 = new Button("Cancel");
            cancel2.setPrefWidth(70);
            cancel2.setLayoutX(205);
            cancel2.setLayoutY(65);
            cancel2.setOnAction(this);

            Text dialog = new Text("Media Item Format:");
            dialog.setLayoutX(15);
            dialog.setLayoutY(45);

            tf2 = new TextField();
            tf2.setPrefHeight(20);
            tf2.setPrefWidth(150);
            tf2.setLayoutX(125);
            tf2.setLayoutY(30);
            format = tf2.getText();

            Pane layout2 = new Pane();
            layout2.getChildren().addAll(tf2, dialog, accept2, cancel2);

            stage2 = new Stage();
            stage2.setTitle("Item Format");
            stage2.setScene(new Scene(layout2, 400, 100));
            stage2.show();
        }

        if (event.getSource() == cancel2) {
            title = null;
            titles.remove(titles.size() - 1);
            stage2.close();
        }

        if (event.getSource() == accept2) {
            format = tf2.getText();
            formats.add(tf2.getText());
            lib.addNewItem(title, format);
            items.getItems().add(title + " (" + format + ")");
            stage2.close();

        }

        if (event.getSource() == checkOut) { //item is being checked out
            item = items.getSelectionModel().getSelectedItems();

            for (int i = 0; i < items.getItems().size(); i++) {
                if (items.getSelectionModel().getSelectedItem()
                        .equals(lib.getItemList().get(i).getTitle() + " (" + lib.getItemList().get(i).getFormat() + ")")

                        && !lib.getItemList().get(i).onLoan) {
                    itemNum = i;

                    accept3 = new Button("Accept");
                    accept3.setPrefWidth(70);
                    accept3.setLayoutX(125);
                    accept3.setLayoutY(65);
                    accept3.setOnAction(this);
                    cancel3 = new Button("Cancel");
                    cancel3.setPrefWidth(70);
                    cancel3.setLayoutX(205);
                    cancel3.setLayoutY(65);
                    cancel3.setOnAction(this);

                    Text dialog = new Text("Loaned To:");
                    dialog.setLayoutX(60);
                    dialog.setLayoutY(45);

                    tf3 = new TextField();
                    tf3.setPrefHeight(20);
                    tf3.setPrefWidth(150);
                    tf3.setLayoutX(125);
                    tf3.setLayoutY(30);

                    Pane layout3 = new Pane();
                    layout3.getChildren().addAll(accept3, cancel3, dialog, tf3);

                    stage3 = new Stage();
                    stage3.setTitle("Check Out");
                    stage3.setScene(new Scene(layout3, 400, 100));
                    stage3.show();

                }
            }
        }

        if (event.getSource() == accept3) {
            loanedTo = tf3.getText();
            lib.getItemList().get(itemNum).loanedTo = loanedTo;
            stage3.close();

            accept4 = new Button("Accept");
            accept4.setPrefWidth(70);
            accept4.setLayoutX(125);
            accept4.setLayoutY(65);
            accept4.setOnAction(this);
            cancel4 = new Button("Cancel");
            cancel4.setPrefWidth(70);
            cancel4.setLayoutX(205);
            cancel4.setLayoutY(65);
            cancel4.setOnAction(this);

            Text dialog = new Text("Date Loaned:");
            dialog.setLayoutX(50);
            dialog.setLayoutY(45);

            tf4 = new TextField();
            tf4.setPrefHeight(20);
            tf4.setPrefWidth(150);
            tf4.setLayoutX(125);
            tf4.setLayoutY(30);

            Pane layout3 = new Pane();
            layout3.getChildren().addAll(accept4, cancel4, dialog, tf4);

            stage4 = new Stage();
            stage4.setTitle("Check Out");
            stage4.setScene(new Scene(layout3, 400, 100));
            stage4.show();

        }

        if (event.getSource() == accept4) {
            stage4.close();
            dateLoaned = tf4.getText();
            lib.getItemList().get(items.getSelectionModel().getSelectedIndex()).dateLoaned = dateLoaned;
            lib.getItemList().get(items.getSelectionModel().getSelectedIndex()).onLoan = true;

            items.getItems().set(items.getSelectionModel().getSelectedIndex(),
                    titles.get(items.getSelectionModel().getSelectedIndex()) + " ("
                            + formats.get(items.getSelectionModel().getSelectedIndex()) + ") loaned to " + loanedTo
                            + " on " + dateLoaned);
        }

        if (event.getSource() == checkIn) {
            for (int i = 0; i < items.getItems().size(); i++) {
                if (items.getSelectionModel().getSelectedItem().contains(lib.getItemList().get(i).getTitle())
                        && items.getSelectionModel().getSelectedItem().contains(lib.getItemList().get(i).getFormat())
                        && lib.getItemList().get(i).onLoan) {
                    itemNum = i;
                    items.getItems().set(items.getSelectionModel().getSelectedIndex(),
                            titles.get(items.getSelectionModel().getSelectedIndex()) + " ("
                                    + formats.get(items.getSelectionModel().getSelectedIndex()) + ")");

                    lib.getItemList().get(items.getSelectionModel().getSelectedIndex()).dateLoaned = null;
                    lib.getItemList().get(items.getSelectionModel().getSelectedIndex()).onLoan = false;

                }
            }
        }

        if (event.getSource() == delete) { //item is being deleted from all lists
            items.getItems().remove(items.getSelectionModel().getSelectedIndex());
            for (int i = 0; i < items.getItems().size(); i++) {
                if (items.getSelectionModel().getSelectedItem().contains(lib.getItemList().get(i).getTitle())
                        && items.getSelectionModel().getSelectedItem().contains(lib.getItemList().get(i).getFormat())) {
                    itemNum = i;
                    lib.getItemList().remove(items.getSelectionModel().getSelectedIndex());
                    items.getItems().remove(items.getSelectionModel().getSelectedIndex());
                    titles.remove(items.getSelectionModel().getSelectedIndex());
                    formats.remove(items.getSelectionModel().getSelectedIndex());

                }
            }
        }

    }

}

Library.java

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class Library {

    private ObservableList<MediaItem> item = FXCollections.observableArrayList();

    public ObservableList<MediaItem> getItemList() {
        return item;
    }

    void addNewItem(String title, String format) {
        item.add(new MediaItem(title, format));

    }

I only recently started programming in Java so my code most likely isn't quite optimized. Any and all help will be greatly appreciated. Thanks.

Body:
This isn't a complete answer, since I didn't run your code, but looking at it I had a couple of suggestions for you.

First off, having your entire class be an ActionEvent and having a single 'handle' method is probably not ideal. I would split up that handle method into separate methods, named like this public void handleAdd(ActionEvent event) and add them to your buttons like this add.setOnAction((ActionEvent e) -> handleAdd(e));.
This makes more sense because each button has a specific action associated with it, and that action is contained in a specific method.

Secondly, do you need four lists and a Library object (which looks to be just a wrapper for another list)? You're trying to keep five different lists in sync with each other, and that's bound to cause problems like the ones you're seeing now as well as headache for you or whomever has to read and maintain the code in the future. Consider instead just having your Library and making your ListView use type MediaItem instead of String and add loanedto and dateloaned as properties of MediaItem, assuming that's a class you wrote yourself. You can still show the titles properly in the ListView by setting a cell factory like this:

items.setCellFactory(new Callback<ListView<MediaItem>, ListCell<MediaItem>>() {
    @Override
    public ListCell<MediaItem> call(ListView<MediaItem> param) {
        return new ListCell<MediaItem>() {
            @Override
            protected void updateItem(MediaItem item, boolean empty) {
                super.updateItem(item, empty);
                if(item != null) {
                    setText(item.getTitle());
                }else{
                    setText("");
                }
            }
        };
    }
});


问:
ContainsKey returns unit and not bool?
It's odd to me that the following code throws a compile time error. I'm not sure why ContainsKey is returning unit. The documentation says it returns bool.

```<code>open System.Collections.Generic

let mydict = new Dictionary<string,'a>()

if(mydict.ContainsKey "mykey") then
    mydict.["mykey"] = newkey
</code>```

error FS0001: This expression was expected to have type
      'bool'     but here has type
      'unit'

Am I missing something here?

答:
if is an expression so both branches must have the same type. If the else branch is not specified an empty one of type unit is inserted. This means your then branch must also have type unit. However mydict.["mykey"] = newkey has type bool. If you want to insert a new value for mykey you should use <- instead of =:

```<code>if(mydict.ContainsKey "mykey") then
    mydict.["mykey"] <- newkey
</code>```

Title: ContainsKey returns unit and not bool?

Body:
It's odd to me that the following code throws a compile time error. I'm not sure why ContainsKey is returning unit. The documentation says it returns bool.

open System.Collections.Generic

let mydict = new Dictionary<string,'a>()

if(mydict.ContainsKey "mykey") then
    mydict.["mykey"] = newkey

error FS0001: This expression was expected to have type
      'bool'     but here has type
      'unit'

Am I missing something here?

Body:
if is an expression so both branches must have the same type. If the else branch is not specified an empty one of type unit is inserted. This means your then branch must also have type unit. However mydict.["mykey"] = newkey has type bool. If you want to insert a new value for mykey you should use <- instead of =:

if(mydict.ContainsKey "mykey") then
    mydict.["mykey"] <- newkey


问:
conditional execution of particular teststep according to result of previous request
I use SOAP UI Free.

I want to verify if previous response returns number (id) and conditionally run particular teststep.
My pseudocode below. How can I achieve this action using groovy?

How can I get response and verify if contains number and returns 200?

How can I extract this number and use it as parameter in next request

How can I compare if response is true?

response sample (200) :

```<code>523455
</code>```

response sample (404) :

```<code>{
   "category": "BUSINESS",
   "code": "NOT_FOUND",
   "description":"Account not found",
   "httpStatus": 404
}
</code>```

1.step GET accountId

2.step GROOVY

```<code>if(accountId is number and returns 200){
    extract this number from json
        run testRunner.testStep("removeAccount) for extracted number
            if(response.equals("true"){

               testRunner.runTestStep("createNewAccount")
      }
}
</code>```

答:
1 - Use a property transfer step :

Target the id using JSONPath (should be something like "$.id")

Set "Set null on missing source" to true

Store it in a custom property of your testCase (e.g. "curId")

Additionally, if you want the Http status code, you should store it with a groovy script in another custom variable using something like this :

```<code>curHeaders = testRunner.testCase.testSteps["Get token"].testRequest.response.getResponseHeaders()

testRunner.testCase.setPropertyValue("http status", curHeaders["#status#"][0])
</code>```

2 - Using a groovy script step :
Retrieve the variables above :

```<code>curId = testRunner.testCase.getPropertyValue("curId")
curHttpStatus = testRunner.testCase.getPropertyValue("http status")
</code>```

Test those variables and run test step "removeAccount" if true:

```<code>if(curId && (curHttpStatus == 'HTTP/1.1 200 OK'))
{
     removeActionTestStep = testRunner.testSteps["removeAccount"]
     removeActionTestStep.run(null, false)

}
</code>```

Note : removeAccount test step should make reference to curId custom variable (e.g. using "${#TestCase#curId}) so it runs with this id

You can add the createAccount part after the removal using jsonSlurper

```<code>removeActionResultJSON = context.expand('${removeAccount#Response}')
removeActionResultJSONSlurper = new groovy.json.JsonSlurper().parseText(removeActionResultJSON )
</code>```

Then target where your response will make it equal true, then use another if - run statement like below.

Hope this helps,

Thomas

Body:
I use SOAP UI Free.

I want to verify if previous response returns number (id) and conditionally run particular teststep.
My pseudocode below. How can I achieve this action using groovy?

How can I get response and verify if contains number and returns 200?

How can I extract this number and use it as parameter in next request

How can I compare if response is true?

response sample (200) :

523455

response sample (404) :

{
   "category": "BUSINESS",
   "code": "NOT_FOUND",
   "description":"Account not found",
   "httpStatus": 404
}

1.step GET accountId

2.step GROOVY

if(accountId is number and returns 200){
    extract this number from json
        run testRunner.testStep("removeAccount) for extracted number
            if(response.equals("true"){

               testRunner.runTestStep("createNewAccount")
      }
}

Body:
1 - Use a property transfer step :

Target the id using JSONPath (should be something like "$.id")

Set "Set null on missing source" to true

Store it in a custom property of your testCase (e.g. "curId")

Additionally, if you want the Http status code, you should store it with a groovy script in another custom variable using something like this :

curHeaders = testRunner.testCase.testSteps["Get token"].testRequest.response.getResponseHeaders()

testRunner.testCase.setPropertyValue("http status", curHeaders["#status#"][0])

2 - Using a groovy script step :
Retrieve the variables above :

curId = testRunner.testCase.getPropertyValue("curId")
curHttpStatus = testRunner.testCase.getPropertyValue("http status")

Test those variables and run test step "removeAccount" if true:

if(curId && (curHttpStatus == 'HTTP/1.1 200 OK'))
{
     removeActionTestStep = testRunner.testSteps["removeAccount"]
     removeActionTestStep.run(null, false)

}

Note : removeAccount test step should make reference to curId custom variable (e.g. using "${#TestCase#curId}) so it runs with this id

You can add the createAccount part after the removal using jsonSlurper

removeActionResultJSON = context.expand('${removeAccount#Response}')
removeActionResultJSONSlurper = new groovy.json.JsonSlurper().parseText(removeActionResultJSON )

Then target where your response will make it equal true, then use another if - run statement like below.

Hope this helps,

Thomas


问:
key error while trying to pass data as a parameter using a json file
code is given below
import time
import unittest
import logging as log
from loggenerator import set_log_params,move_latest_log_to_persistent_file
from parse_test_json import get_test_params
from scrapping import SC

```<code>    class ScrapeTest(unittest.TestCase):
        def setup(self):
            self.start_time=time.time()
        def teardown(self):
            self.end_time=time.time()
        def test_SC_scrape(self):
            try:
                test_name="test scrape"
                set_log_params(log_file=test_name,level=log.INFO)
                log.info("step1:set all test params")
                test_params = get_test_params(self.__class__.__name__, test_name=test_name)
                log.info("step 2:set")
                ik=SC()
                log.info("step3:calling scrapper for")
                log.debug(ik.parseURL(party_name=test_params["party_name"],start_date=test_params["start_date"],end_date=test_params["end_date"]))

            except Exception as e:
                raise e
            move_latest_log_to_persistent_file(log_file=test_name)
</code>```

####

```<code>    import json, os
    from builtins import *
    def get_test_params(test_class_name = None, test_name = None):
        dir_path = os.path.dirname(os.path.realpath(__file__))
        file_path = "/test_param_jsons/" + test_class_name + "params.json"
        json_file = dir_path + file_path
        with open(json_file) as test_data:
            test_json = json.load(test_data)
            return test_json[test_class_name][test_name]
</code>```

this function is raising error key error.

Body:
code is given below
import time
import unittest
import logging as log
from loggenerator import set_log_params,move_latest_log_to_persistent_file
from parse_test_json import get_test_params
from scrapping import SC

    class ScrapeTest(unittest.TestCase):
        def setup(self):
            self.start_time=time.time()
        def teardown(self):
            self.end_time=time.time()
        def test_SC_scrape(self):
            try:
                test_name="test scrape"
                set_log_params(log_file=test_name,level=log.INFO)
                log.info("step1:set all test params")
                test_params = get_test_params(self.__class__.__name__, test_name=test_name)
                log.info("step 2:set")
                ik=SC()
                log.info("step3:calling scrapper for")
                log.debug(ik.parseURL(party_name=test_params["party_name"],start_date=test_params["start_date"],end_date=test_params["end_date"]))

            except Exception as e:
                raise e
            move_latest_log_to_persistent_file(log_file=test_name)

####

    import json, os
    from builtins import *
    def get_test_params(test_class_name = None, test_name = None):
        dir_path = os.path.dirname(os.path.realpath(__file__))
        file_path = "/test_param_jsons/" + test_class_name + "params.json"
        json_file = dir_path + file_path
        with open(json_file) as test_data:
            test_json = json.load(test_data)
            return test_json[test_class_name][test_name]

this function is raising error key error.

Body:
this should work as long as you have a json file available at: <SCRIPT_PATH>/test_param_jsons/MyClass_params.json

Also, in order to avoid KeyError you'll need to ensure that your input json file contains key: value, test_class_name : test_name

import json, os
from builtins import *

class MyClass:
        def get_test_params (self, test_class_name = None, test_name = None):
              with open(os.path.join(os.path.dirname(__file__),"test_param_jsons\\params.json"), 'r') as test_data:
                        test_json = json.load (test_data)
                        try:
                                return test_json[test_class_name][test_name]
                        except KeyError as e:
                                print ('KeyError: {}'.format (e))

        def mymain(self, test_name):
                ''' mymain is defined to accomodate your requirement to use __class__ as a parameter '''
                test_params = self.get_test_params (self.__class__.__name__, test_name = test_name)
                return test_params

if __name__ == '__main__':
        test_name = 'sth'
        myclass = MyClass ()
        result = myclass.mymain (test_name)
        print (result)


问:
mail/mailx: querying the default MX records that are used?
Is there a way to run a program or script on a linux box to see what MX records are used by default when sending outgoing email from mail or mailx? I'm writing a utility that is going to be run on a large, random set of linux machines, and I'd like that utility to automatically figure out the MX hosts that will actually be used on each machine when mail or mailx are used.

I know I can look at the machine's mail server's config file to see what the setting is for outgoing email, but each host might be using a different email server (sendmail, postfix, exim, etc.), and I don't want to write a utility that searches for each one and then tries to parse whatever config files it might find.

If I knew the algorithm that mail and mailx use for deciding what server to use for outgoing mail, I could then replicate that algorithm within my own utility.

Do mail and mailx just try 127.0.0.1? Or do they do something like getting the current host name and then using a DNS query to find the MX records for that host, or if none exist, to use the info from the A record? Or do they simply use the sendmail program (in which case, I'd need to figure out which outgoing server that sendmail chooses)? ... or what?

Thank you very much.

答:
I have found the source code for mail and for a version of mailx, and it seems like mail just uses the sendmail executable, and mailx allows an optional specification of an SMTP server, or if that's absent, it also uses the sendmail executable.

This doesn't help me much, and so I think I need to use my own algorithm. Here's what I have come up with (pseudo-code). Does anyone see any problems with this? ...

```<code>set `mxs` = list of IP addr(s) of current hostname's MX record(s)
if `mxs` cannot be determined
    set `mxs` = one-element list of IP addr of current hostname's A record
    if `mxs` cannot be determined
        set `mxs` = one-element list of current host's IP address
        if for some reason `mxs` still can't be determined
            set `mxs` = one-element list containing '127.0.0.1'
        end if
    end if
end if
</code>```

In the end, mxs will contain a list of IP addresses upon which the current machine's SMTP server is likely to be listening. It's still possible that none of these will actually work for sending email, but that's OK for my purposes.

In the real world, I'll use a set rather than a list, to avoid duplicate entries.

How does this look?

Body:
I have found the source code for mail and for a version of mailx, and it seems like mail just uses the sendmail executable, and mailx allows an optional specification of an SMTP server, or if that's absent, it also uses the sendmail executable.

This doesn't help me much, and so I think I need to use my own algorithm. Here's what I have come up with (pseudo-code). Does anyone see any problems with this? ...

set `mxs` = list of IP addr(s) of current hostname's MX record(s)
if `mxs` cannot be determined
    set `mxs` = one-element list of IP addr of current hostname's A record
    if `mxs` cannot be determined
        set `mxs` = one-element list of current host's IP address
        if for some reason `mxs` still can't be determined
            set `mxs` = one-element list containing '127.0.0.1'
        end if
    end if
end if

In the end, mxs will contain a list of IP addresses upon which the current machine's SMTP server is likely to be listening. It's still possible that none of these will actually work for sending email, but that's OK for my purposes.

In the real world, I'll use a set rather than a list, to avoid duplicate entries.

How does this look?


问:
QT UDP Socket Returning Empty Datagrams
Let me start this by saying this is more than likely a UDP configuration issue than a QT issue.

I am playing a PCAP file using UDP replay on an ubuntu computer to a windows PC.  When I check the UDP data via wireshark, I can see all of the associated data within each packet.  But, when I use my QT program, I get the packet to come through, with the proper length but there is no data inside the packet, just 0s.

I have the IP address of the Windows PC set to be on the same sub-net as the ubuntu source PC as well as the proper port configured.

I am using essentially the QT Multicast Receiver example, here: http://doc.qt.io/qt-5/qtnetwork-multicastreceiver-example.html

Any thoughts?

Edit:

My apologies for not including proper code.

```<code>void Receiver::processPendingDatagrams()
 {
    while (udpSocket->hasPendingDatagrams()) {
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        udpSocket->readDatagram(datagram.data(), datagram.size());
        statusLabel->setText(tr("Received datagram: \"%1\"")
                             .arg(datagram.data()));
    }
}
</code>```

The datagram.data() byte array is simply filled with zeros, no matter what the packet actually contained.

Thoughts?

答:
Your code conveniently ignores the size of the received datagram returned by readDatagram; that's one source of possible errors. Things will be much easier by using receiveDatagram instead:

```<code>void Receiver::processPendingDatagrams()
{
  while (udpSocket->hasPendingDatagrams()) {
    auto datagram = udpSocket->receiveDatagram();
    statusLabel->setText(tr("Received datagram: \"%1\"")
                         .arg(QString::fromLatin1(datagram.data().toHex())));
  }
}
</code>```

Now the datagram's contents are hex-encoded and it's easy to tell exactly what's in it and how long it is. It's generally speaking a very bad idea to pass unsanitized datagram contents as arguments to string output methods, as that makes any UTF-8 decoder bugs potentially exploitable. Use toHex() to protect yourself from that. Since data() here is not char * data() but QByteArray data(), the display will show exactly the length received.

Title: QT UDP Socket Returning Empty Datagrams

Body:
Let me start this by saying this is more than likely a UDP configuration issue than a QT issue.

I am playing a PCAP file using UDP replay on an ubuntu computer to a windows PC.  When I check the UDP data via wireshark, I can see all of the associated data within each packet.  But, when I use my QT program, I get the packet to come through, with the proper length but there is no data inside the packet, just 0s.

I have the IP address of the Windows PC set to be on the same sub-net as the ubuntu source PC as well as the proper port configured.

I am using essentially the QT Multicast Receiver example, here: http://doc.qt.io/qt-5/qtnetwork-multicastreceiver-example.html

Any thoughts?

Edit:

My apologies for not including proper code.

void Receiver::processPendingDatagrams()
 {
    while (udpSocket->hasPendingDatagrams()) {
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        udpSocket->readDatagram(datagram.data(), datagram.size());
        statusLabel->setText(tr("Received datagram: \"%1\"")
                             .arg(datagram.data()));
    }
}

The datagram.data() byte array is simply filled with zeros, no matter what the packet actually contained.

Thoughts?

Body:
Your code conveniently ignores the size of the received datagram returned by readDatagram; that's one source of possible errors. Things will be much easier by using receiveDatagram instead:

void Receiver::processPendingDatagrams()
{
  while (udpSocket->hasPendingDatagrams()) {
    auto datagram = udpSocket->receiveDatagram();
    statusLabel->setText(tr("Received datagram: \"%1\"")
                         .arg(QString::fromLatin1(datagram.data().toHex())));
  }
}

Now the datagram's contents are hex-encoded and it's easy to tell exactly what's in it and how long it is. It's generally speaking a very bad idea to pass unsanitized datagram contents as arguments to string output methods, as that makes any UTF-8 decoder bugs potentially exploitable. Use toHex() to protect yourself from that. Since data() here is not char * data() but QByteArray data(), the display will show exactly the length received.


问:
Combining two async results from MongoDB collection in NodeJS
I'm sure this was asked before, but for some reason I am not able to make it work. Started using NodeJS with Mongoose just lately (getting used to doing everything async)
One of the things I'm trying to achieve is combining two results from two different collections according to some logic.

So assuming I have this get function, it should go fetch asynchronously all skills (for example), then from another collection, i should fetch all specific user skills, and combine them into one set of results, which will add a "isSelected:true" property when it's found in the userSkills collection. This is written in ES6:

```<code>exports.get = (req, res) => {

const combineSkills = (allSkills)=>{
    const { userid } = req.headers;
     return UserSkills.GetUserSkills(userid).then((response)=>{
        for(var i=0;i<=response.length-1;i++){
           var userSkill = response[i];
           var found = allSkills.filter(e=>e.id==userSkill.skillId);
           if(found.length>0){
                found.isSelected=true;
           }
        }
        return allSkills;
    });
}

const respond = (response) => {
    res.json({
        ReturnCode: 2,
        ReturnObject: response
    })
}

// error occured
const onError = (error) => {
    res.status(403).json({
        ReturnCode: 0,
        ReturnObject: error.message
    })
}

Skills.GetAll()
    .then(combineSkills)
    .then(respond)
    .catch(onError)
</code>```

}

As you can see, I'm trying to call Skills.GetAll() skills, then get results to combineSkills object, do some logic and return the new object.
I know my problem is in the combineSkills function, where my return statement returns the object before the logic change.

My question is what is the right syntax for such scenario?

答:
filter function return an array, you have to return the needed skills using find method like :

```<code>  const combineSkills = (allSkills) => {
      const { userid } = req.headers;
      return UserSkills.GetUserSkills(userid).then((response) => {
        for (var i = 0; i <= response.length - 1; i++) {
          var userSkill = response[i];
          var found = allSkills.find(e => e.id == userSkill.skillId);
          if (found) {
            found.isSelected = true;
          }
        }
        return allSkills;
      });
    }
</code>```

Body:
I'm sure this was asked before, but for some reason I am not able to make it work. Started using NodeJS with Mongoose just lately (getting used to doing everything async)
One of the things I'm trying to achieve is combining two results from two different collections according to some logic.

So assuming I have this get function, it should go fetch asynchronously all skills (for example), then from another collection, i should fetch all specific user skills, and combine them into one set of results, which will add a "isSelected:true" property when it's found in the userSkills collection. This is written in ES6:

exports.get = (req, res) => {

const combineSkills = (allSkills)=>{
    const { userid } = req.headers;
     return UserSkills.GetUserSkills(userid).then((response)=>{
        for(var i=0;i<=response.length-1;i++){
           var userSkill = response[i];
           var found = allSkills.filter(e=>e.id==userSkill.skillId);
           if(found.length>0){
                found.isSelected=true;
           }
        }
        return allSkills;
    });
}

const respond = (response) => {
    res.json({
        ReturnCode: 2,
        ReturnObject: response
    })
}

// error occured
const onError = (error) => {
    res.status(403).json({
        ReturnCode: 0,
        ReturnObject: error.message
    })
}

Skills.GetAll()
    .then(combineSkills)
    .then(respond)
    .catch(onError)

}

As you can see, I'm trying to call Skills.GetAll() skills, then get results to combineSkills object, do some logic and return the new object.
I know my problem is in the combineSkills function, where my return statement returns the object before the logic change.

My question is what is the right syntax for such scenario?

Body:
filter function return an array, you have to return the needed skills using find method like :

  const combineSkills = (allSkills) => {
      const { userid } = req.headers;
      return UserSkills.GetUserSkills(userid).then((response) => {
        for (var i = 0; i <= response.length - 1; i++) {
          var userSkill = response[i];
          var found = allSkills.find(e => e.id == userSkill.skillId);
          if (found) {
            found.isSelected = true;
          }
        }
        return allSkills;
      });
    }


问:
Where should i put my Admin control panel?
I'm building a community site using PHP/MySQL, I was thinking of just letting admins login where normal users log in, and then present them with a "Admin Control Panel" link. So that the control panel platform is hosted on the same site. Is this secure? Should there be a different "gateway" for admins instead i.e. http://admin.example.com? Or should i host the Control Panel on an entirely different domain?

Thanks for your time.

答:
I log in everyone with a correct username/password. But I also have an additional column in my users table for admins with (hard to guess) values for "yes" and "no". If a user logs in, I'm also checking this column, and if the value is "yes" I check another table for admin privileges.

But the admin panel itself is located in a regular folder, such as: /admin. As you would expect.

ContentLicense: CC BY-SA 2.5


问:
Longest Increasing Numeral sub-sequence of array
We got this problem in our course that no one who I had talked to solved it. I would like for some help. So here's the problem:

Let A be array of length n which contains n digits (digit is between 0-9).
A numeral sub-sequence of A is a sequence of positive numbers which their digits compose a sub-sequence of A, when all digits of a certain number in the sequence appear in a row in A.

For example: the sequence 13,1,345,89,23 is a numeral sub-sequence of input array A:
[1,3,5,1,2,3,4,5,8,9,4,5,2,3]

Length of a numeral sub-sequence is the amount of numbers which appear in it (in the example above: 5)
Numeral sub-sequence is increasing if every number in the sequence is bigger than the number before it.

The request is to find an algorithm in dynamic programming approach (based on recursive formula) that finds the longest increasing numeral sub-sequence of an input array A.

Thanks in advance for all helpers!

答:
Look at the first digit in the array. Either this digit is not part of a number in your number sequence or it is. If it is, the number could have 1, 2, ..., n digits. For each guess, return:

not in a number: return f(array[2...n], -1)

1st digit of 1-digit number: return array[1] union f(array[2...n], number(array[1]))

1st digit of 2-digit number: return array[1...2] union f(array[3...n], number(array[1...2]))

1st digit of 3-digit number: return array[1...3] union f(array[4...n], number(array[1...3]))

...

1st digit of n-digit number: return array[1...n]

There are some optimizations you can do here to skip some steps along the way.

f(array[1...k], x) = f(array[1...k], y) if the smallest choice for the next number in the sequence given hypothetical last numbers x and y is the same. So, if the smallest choice for the next number in array[1...k] is the same for x and y, and we already computed the value of f for x, we can reuse that value for y.

f(array[1...k], x) = c + f(array[2...k], x) whenever array[1] = 0, where c = 1 if x < 0 and c = 0 if x >= 0. That is, we can ignore leading zeroes except possibly a leading zero at the beginning of the array which should always be chosen as our first one-digit number.

when deciding whether a digit will be the first digit of a k-digit number, if you never choose leading zeroes, you know an upper bound on the number of remaining numbers in your sequence is given by n/k, since any numbers chosen after this one will need to be at least k digits long. If you remember the longest sequence you've seen so far, you can recognize paths that have no hope of doing better than what you've seen and ignore them.

if an array has at least k(k+1)/2 non-zero digits in it, there is a number sequence of length at least k obtained by taking numbers with 1, 2, ..., k non-zero digits sequentially left to right. So, if you pre-compute this value, you can potentially avoid some paths right off the bat.

Here's rough pseudocode with the optimizations discussed:

```<code>solve(array[1...n])

    z = number of non-zero entries in array
    last_number = -1
    min_soln = floor((sqrt(1 + 8z) - 1) / 2)
    return solve_internal(array[1...n], min_soln, last_number)

memo = {}

solve_internal(array[1...n], min_soln, last_number)

    // ignore potentially leading zeroes except the first one
    if array[1] = 0 then
        if last_number < 0 then
            return {0} union solve_internal(array[2...n], min_soln - 1, 0)
        else then
            return solve_internal(array[2...n], min_soln, last_number)

    // abort since we don't have enough digits left to get a solution
    if floor(n / #digits(last_number)) < min_soln return []

    // look up current situation in previous partial solutions
    z = smallest number formable in array greater than last_number
    if memo contains (n, z) then
        return memo[n, z]

    soln = {}
    for k = 1 to n do
        soln_k = solve_internal(array[k+1...n], min_soln - 1, array[1...k])
        if |soln_k| > |soln| then
            soln = soln_k
            min_soln = |soln|

    memo[n, z] = soln
    return soln
</code>```

Body:
Look at the first digit in the array. Either this digit is not part of a number in your number sequence or it is. If it is, the number could have 1, 2, ..., n digits. For each guess, return:

not in a number: return f(array[2...n], -1)

1st digit of 1-digit number: return array[1] union f(array[2...n], number(array[1]))

1st digit of 2-digit number: return array[1...2] union f(array[3...n], number(array[1...2]))

1st digit of 3-digit number: return array[1...3] union f(array[4...n], number(array[1...3]))

...

1st digit of n-digit number: return array[1...n]

There are some optimizations you can do here to skip some steps along the way.

f(array[1...k], x) = f(array[1...k], y) if the smallest choice for the next number in the sequence given hypothetical last numbers x and y is the same. So, if the smallest choice for the next number in array[1...k] is the same for x and y, and we already computed the value of f for x, we can reuse that value for y.

f(array[1...k], x) = c + f(array[2...k], x) whenever array[1] = 0, where c = 1 if x < 0 and c = 0 if x >= 0. That is, we can ignore leading zeroes except possibly a leading zero at the beginning of the array which should always be chosen as our first one-digit number.

when deciding whether a digit will be the first digit of a k-digit number, if you never choose leading zeroes, you know an upper bound on the number of remaining numbers in your sequence is given by n/k, since any numbers chosen after this one will need to be at least k digits long. If you remember the longest sequence you've seen so far, you can recognize paths that have no hope of doing better than what you've seen and ignore them.

if an array has at least k(k+1)/2 non-zero digits in it, there is a number sequence of length at least k obtained by taking numbers with 1, 2, ..., k non-zero digits sequentially left to right. So, if you pre-compute this value, you can potentially avoid some paths right off the bat.

Here's rough pseudocode with the optimizations discussed:

solve(array[1...n])

    z = number of non-zero entries in array
    last_number = -1
    min_soln = floor((sqrt(1 + 8z) - 1) / 2)
    return solve_internal(array[1...n], min_soln, last_number)

memo = {}

solve_internal(array[1...n], min_soln, last_number)

    // ignore potentially leading zeroes except the first one
    if array[1] = 0 then
        if last_number < 0 then
            return {0} union solve_internal(array[2...n], min_soln - 1, 0)
        else then
            return solve_internal(array[2...n], min_soln, last_number)

    // abort since we don't have enough digits left to get a solution
    if floor(n / #digits(last_number)) < min_soln return []

    // look up current situation in previous partial solutions
    z = smallest number formable in array greater than last_number
    if memo contains (n, z) then
        return memo[n, z]

    soln = {}
    for k = 1 to n do
        soln_k = solve_internal(array[k+1...n], min_soln - 1, array[1...k])
        if |soln_k| > |soln| then
            soln = soln_k
            min_soln = |soln|

    memo[n, z] = soln
    return soln


问:
Handling Recursive models in EF 6
For the life of me, I can not get this to persist (And reload) with EF 6....

```<code>public class SillyModel
{
    public SillyModel()
    {
        Id = Guid.NewGuid();
        Children = new List<SillyModel>();
    }
    [Key]
    public virtual Guid Id { get; set; }

    public virtual string Value { get; set; }

    public virtual List<SillyModel> Children { get; set; }
}
</code>```

}

答:
Works fine for me (although as @Daniel Lorenz points out having a Foreign Key property is a best-practice):</p>

```<code>using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Linq;

namespace Ef6Test
{

    public class SillyModel
    {
        public SillyModel()
        {
            Id = Guid.NewGuid();
            Children = new List<SillyModel>();
        }
        [Key]
        public virtual Guid Id { get; set; }

        public virtual string Value { get; set; }

        public virtual ICollection<SillyModel> Children { get; } = new HashSet<SillyModel>();
    }
    class Db : DbContext
    {
        public DbSet<SillyModel> SillyModels { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

        }
    }

    class Program
    {
        static void Main(string[] args)
        {

            Database.SetInitializer(new DropCreateDatabaseAlways<Db>());

            using (var db = new Db())
            {

                db.Database.Log = m => Console.WriteLine(m);
                db.Database.Initialize(true);

                var e = new SillyModel();

                for (int i = 0; i < 10; i++)
                {
                    e.Children.Add(new SillyModel());
                }

                db.SillyModels.Add(e);
                db.SaveChanges();

            }

            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }
}
</code>```

Title: Handling Recursive models in EF 6

Body:
For the life of me, I can not get this to persist (And reload) with EF 6....

public class SillyModel
{
    public SillyModel()
    {
        Id = Guid.NewGuid();
        Children = new List<SillyModel>();
    }
    [Key]
    public virtual Guid Id { get; set; }

    public virtual string Value { get; set; }

    public virtual List<SillyModel> Children { get; set; }
}

}

Body:
Works fine for me (although as @Daniel Lorenz points out having a Foreign Key property is a best-practice):

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
using System.Linq;

namespace Ef6Test
{

    public class SillyModel
    {
        public SillyModel()
        {
            Id = Guid.NewGuid();
            Children = new List<SillyModel>();
        }
        [Key]
        public virtual Guid Id { get; set; }

        public virtual string Value { get; set; }

        public virtual ICollection<SillyModel> Children { get; } = new HashSet<SillyModel>();
    }
    class Db : DbContext
    {
        public DbSet<SillyModel> SillyModels { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

        }
    }

    class Program
    {
        static void Main(string[] args)
        {

            Database.SetInitializer(new DropCreateDatabaseAlways<Db>());

            using (var db = new Db())
            {

                db.Database.Log = m => Console.WriteLine(m);
                db.Database.Initialize(true);

                var e = new SillyModel();

                for (int i = 0; i < 10; i++)
                {
                    e.Children.Add(new SillyModel());
                }

                db.SillyModels.Add(e);
                db.SaveChanges();

            }

            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }
}


问:
How to change a single macro in many excel workbooks?
I have to make a minor change to an existing macro that is used in >100 XLSM files. The macro is saved locally in the files and has the same name in all files.
Is there a way to automate this?

I know it would have been better to store this macro in a separate sheet...
The reason for the request is exactly that we want to switch to a central macro and change the 'local' macro code to call the 'central' one.

答:
I had the problem with Sub Workbook_BeforeSave: In a good number of old files this function prevented saving it if the Excel version was not Excel 2007. (i.e. even with Excel 2013 or 2016 it would not save the file).

It was simple enough to DELETE the old Sub Workbook_BeforeSave but Excel (at least Excel 2016) acted up when the file was saved to another folder (.SaveAs) right after removing the sub ("Excel has stopped working..."). I then tried not to remove the whole sub but just it's content (all lines between 'Sub' and 'End Sub'; that cause Excel to stall.

Also re-compiling with

```<code>   Dim objVBECommandBar            As Object
   Dim compileMe                   As Object
       Set objVBECommandBar = Application.VBE.CommandBars
       Set compileMe = objVBECommandBar.FindControl(Type:=msoControlButton, ID:=578)
       compileMe.Execute 'the project should hence be compiled
</code>```

...didn't help. I suspect a mismatch of the Excel function address table after the manipulation of the code module.

What did help was commenting out the content of Sub Workbook_BeforeSave(...), i.e. keeping

```<code>    Sub Workbook_BeforeSave (...)
</code>```

and

```<code>    End Sub
</code>```

...and make everything inbetween as comment.

```<code>    Function CommentOutProcedureContent(filename As String, moduleName As String, procName As String) As Variant
    Dim module      As CodeModule
    Dim start       As Long
    Dim realStart   As Long
    Dim Lines       As Long
    Dim rowIdx      As Long
    Dim thisLine    As String
    Dim tmpStr      As String

        Set module = Workbooks(filename).VBProject.VBComponents(moduleName).CodeModule
        On Error Resume Next
        Err.Clear
        With module
            start = .ProcStartLine(procName, vbext_pk_Proc)
            If Err.Number = 0 Then
                Lines = .ProcCountLines(procName, vbext_pk_Proc)
                ' find the real 'function' or 'sub' beginning
                realStart = start
                If .Find("Sub " & procName, realStart, 1, start + Lines, -1) Then
                    '=> realStart now has the real line number
                ElseIf .Find("Function " & procName, realStart, 1, start + Lines, -1) Then
                    '=> realStart now has the real line number
                Else
                    Err.Raise 999
                End If
                If Err.Number = 0 Then
                    For rowIdx = (realStart + 1) To (Lines + start - 2)
                        tmpStr = module.Lines(rowIdx, 1)
                        .DeleteLines rowIdx
                        .InsertLines rowIdx, "'" & tmpStr
                    Next rowIdx
                End If
            End If
        End With

        CommentOutProcedureContent = Err.Number
        On Error GoTo 0
    End Function
</code>```

The need for 2 variables, start and realStart, comes from the fact that module.ProcStartLine(...) returns the next line number after the 'End Sub' of the previous function/sub and not the line number of "Sub Workbook_BeforeSave(...)".

So the upper layer looks like this :

```<code>    Function DisableWorkbookBeforeSave(filename As String) As Variant
    Const thisFunction = "DisableWorkbookBeforeSave"
    Dim objVBECommandBar            As Object
    Dim compileMe                   As Object
    Dim varTMP                      As Variant
    Dim errMsg                      As String
        Application.DisplayAlerts = False
        errMsg = ""
        varTMP = CommentOutProcedureContent(filename, "ThisWorkbook", "Workbook_BeforeSave")
        If varTMP = 0 Then ' everything's ok
            Application.Workbooks(LDRFilename).Activate
            Set objVBECommandBar = Application.VBE.CommandBars
            Set compileMe = objVBECommandBar.FindControl(Type:=msoControlButton, ID:=578)
            compileMe.Execute 'the project should hence be compiled
        Else
            errMsg = thisFunction & " ended with ERROR! Commenting out Sub Workbook_BeforeSave" _
                                  & " in LDR >" & LDRFilename & "< failed." _
                                  & " with error " & Err.Number & "(" & Err.Description & ")"
            write2log errMsg, 1
            MsgBox errMsg
        End If
        DisableWorkbookBeforeSave = varTMP
    End Function
</code>```

Body:
I had the problem with Sub Workbook_BeforeSave: In a good number of old files this function prevented saving it if the Excel version was not Excel 2007. (i.e. even with Excel 2013 or 2016 it would not save the file).

It was simple enough to DELETE the old Sub Workbook_BeforeSave but Excel (at least Excel 2016) acted up when the file was saved to another folder (.SaveAs) right after removing the sub ("Excel has stopped working..."). I then tried not to remove the whole sub but just it's content (all lines between 'Sub' and 'End Sub'; that cause Excel to stall.

Also re-compiling with

   Dim objVBECommandBar            As Object
   Dim compileMe                   As Object
       Set objVBECommandBar = Application.VBE.CommandBars
       Set compileMe = objVBECommandBar.FindControl(Type:=msoControlButton, ID:=578)
       compileMe.Execute 'the project should hence be compiled

...didn't help. I suspect a mismatch of the Excel function address table after the manipulation of the code module.

What did help was commenting out the content of Sub Workbook_BeforeSave(...), i.e. keeping

    Sub Workbook_BeforeSave (...)

and

    End Sub

...and make everything inbetween as comment.

    Function CommentOutProcedureContent(filename As String, moduleName As String, procName As String) As Variant
    Dim module      As CodeModule
    Dim start       As Long
    Dim realStart   As Long
    Dim Lines       As Long
    Dim rowIdx      As Long
    Dim thisLine    As String
    Dim tmpStr      As String

        Set module = Workbooks(filename).VBProject.VBComponents(moduleName).CodeModule
        On Error Resume Next
        Err.Clear
        With module
            start = .ProcStartLine(procName, vbext_pk_Proc)
            If Err.Number = 0 Then
                Lines = .ProcCountLines(procName, vbext_pk_Proc)
                ' find the real 'function' or 'sub' beginning
                realStart = start
                If .Find("Sub " & procName, realStart, 1, start + Lines, -1) Then
                    '=> realStart now has the real line number
                ElseIf .Find("Function " & procName, realStart, 1, start + Lines, -1) Then
                    '=> realStart now has the real line number
                Else
                    Err.Raise 999
                End If
                If Err.Number = 0 Then
                    For rowIdx = (realStart + 1) To (Lines + start - 2)
                        tmpStr = module.Lines(rowIdx, 1)
                        .DeleteLines rowIdx
                        .InsertLines rowIdx, "'" & tmpStr
                    Next rowIdx
                End If
            End If
        End With

        CommentOutProcedureContent = Err.Number
        On Error GoTo 0
    End Function

The need for 2 variables, start and realStart, comes from the fact that module.ProcStartLine(...) returns the next line number after the 'End Sub' of the previous function/sub and not the line number of "Sub Workbook_BeforeSave(...)".

So the upper layer looks like this :

    Function DisableWorkbookBeforeSave(filename As String) As Variant
    Const thisFunction = "DisableWorkbookBeforeSave"
    Dim objVBECommandBar            As Object
    Dim compileMe                   As Object
    Dim varTMP                      As Variant
    Dim errMsg                      As String
        Application.DisplayAlerts = False
        errMsg = ""
        varTMP = CommentOutProcedureContent(filename, "ThisWorkbook", "Workbook_BeforeSave")
        If varTMP = 0 Then ' everything's ok
            Application.Workbooks(LDRFilename).Activate
            Set objVBECommandBar = Application.VBE.CommandBars
            Set compileMe = objVBECommandBar.FindControl(Type:=msoControlButton, ID:=578)
            compileMe.Execute 'the project should hence be compiled
        Else
            errMsg = thisFunction & " ended with ERROR! Commenting out Sub Workbook_BeforeSave" _
                                  & " in LDR >" & LDRFilename & "< failed." _
                                  & " with error " & Err.Number & "(" & Err.Description & ")"
            write2log errMsg, 1
            MsgBox errMsg
        End If
        DisableWorkbookBeforeSave = varTMP
    End Function