Wix DTF Custom Action Equivalent of WcaAddTempRecord - wix

I am trying to use Wix DTF custom action to write MSI runtime session values to registry.
This i wanted to achieve by adding temporary record to "Registry" table in the database.
Since c++ had a WcaAddTempRecord method to achieve this, really wanted to know is there any equivalent method in DTF.
Note: I tried using Session.Database.OpenView to insert the record, but consistently i am getting update failed error, due to session database readonly property.
Can someone please suggest the best approach for this situation?

The MSI database is read-only during the installation. So you cannot add permanent rows. However, you can insert temporary rows. Once you get the View back from the Session.Database.OpenView() then use the InsertTemporary() method on the View object to add temporary rows.
That's how WcaAddTempRecord() gets the Temp in its name. :)

This is my "go to" helper method:
private static void InsertTempRecord(Session session, string tableName, Object[] objects)
{
Database db = session.Database;
string sqlInsertSring = db.Tables[tableName].SqlInsertString + " TEMPORARY";
session.Log("SqlInsertString is {0}", sqlInsertSring);
View view = db.OpenView(sqlInsertSring);
view.Execute(new Record(objects));
view.Close();
}
For more information see:
Dynamic Windows Installer UI

Related

adding new document to CouchDb using ArmChair throws exception Object reference not set to an instance of an object

Iam using couch-db version 2.2.0
and I want to make crud operations on couchdb database using .Net
so I installed Armchair.Core Nuget Package version 0.11.2
and in order to add d a new document I, followed the code that is mentioned in
not finished wiki yet
https://bitbucket.org/dboneslabs/arm-chair/wiki/main-api/session-api.md
Database mydatabase = new Database("TestDb",newConnection("http://localhost:5984"));
using (var session = mydatabase.CreateSession())
{
var author = new Person("Jone");
session.Add(author);// NOTE: If no Id has been assigned before the instance is added to the Session, then ArmChair will assign it. After the object is committed to the database, the revision will then be set onto the instance
session.Commit();
}
but I still getting the error
Object reference not set to an instance of an object.
also mydatabase variable mentioned in previous code has values null for Connection and DataBase Parameters even though i passed them in the constructor as it doesn't connect to couchdb database at all and never tries to create database TestDb
any help please ,are there any wrong calls in my code
ArmChair connects to an existing database and does not create one.
if you want to create a database, have a look a look at the sample application, in the Autofac registration there is a method which ensures that there is a database created.
https://bitbucket.org/dboneslabs/arm-chair/src/bd4e70d6c51d8b45cfb89eb65ecf81a4ecefb691/samples/todo/Todo.Service/Infrastructure/Modules/DataAccessModule.cs#lines-62
its not the pretty-est of code but works.

How do you see what SQL IronSpeed sends to the database?

I'm using IronSpeed Designer 12.2 and trying to write custom SQL in a WhereClause override. The custom SQL I wrote and submitted in the WhereClause is throwing an SQL exception, but I can't see the SQL IronSpeed is sending to the database. Without the SQL, I cannot troubleshoot.
I can't find where the SQL is submitted to the database, such as by an ExecuteReader method call.
I'm using a statement like this:
if (MiscUtils.IsValueSelected(this.MyFilter)) {
String sql = "(EXISTS (SELECT TOP 1 CompanyId FROM Collateral as c WHERE CODE = '{0}' AND c.CompanyId = Company.CompanyId))";
wc.iAND(String.Format(sql, this.MyFilter.SelectedValue));
}
I know my WhereClause SQL is correct when used outside of IronSpeed because I copy-pasted it from a query working directly in MSSQL. However I can't see how IronSpeed combines it with its internally-generated SQL after it becomes a WhereClause.
I'm hoping someone has experience with this issue and can point me in the right direction. Thanks for the help!
If you look for answer long enough, you can find it yourself. Here's how I found you can examine the SQL sent to the database:
Go to C:\Program Files\Iron Speed\Designer v12.2.0.
Copy the BaseClasses folder to the root of my IronSpeed solution folder.
Add the existing BaseClasses project to the IronSpeed solution.
Delete the existing references to baseclasses.dll from the projects in the IronSpeed solution (I'm using a web app rather than web site project).
Add references to the BaseClasses project now included in the solution.
Open the file MicrosoftDynamicSQLAdapter.vb.
In method GetRecordValuesEx(...), go to line 1514 statement "reader = SqlTransaction.ExecuteReader(myCommand, cmdBehavior)" and set a breakpoint on this line.
Run the project. When the breakpoint is hit, examine the command of myCommand object.

How to change ProductCode using Microsoft.Deployment.WindowsInstaller?

In a deployment scenario we had the idea to allow an administrator to change properties in a installer and the ProductCode. The administrator should then push out the newer msi using a GPO policy. Since this now would be a MajorUpgrade the old version, with the old properties, should be uninstalled and the new one, with the new properties, should be installed.
However updating the ProductCode doesn't work.
When executing
db.Execute("UPDATE Property SET Value = '{30571D61-8994-449B-9725-90760DFE0467}' WHERE Property = 'ProductCode'")
an exception is thrown.
[Microsoft.Deployment.WindowsInstaller.InstallerException] = {"Function failed during execution. Database: C:\..\MyInstaller.msi Table(s) Update failed."}
What should I do to upgrade the ProductCode?
(Is it even possible?)
Edit:
If the table is opened as ReadOnly, it will show error.
Which was the case here.
The SDK documentation states that ExecuteScalar can only be used for a SELECT statement that returns a single result. Instead you should use the Execute method.
using (Database database = new Database(#"C:\MSM\ISWIX.MSI", DatabaseOpenMode.Direct))
{
database.Execute("UPDATE Property SET Value = '{00000000-0000-0000-0000-000000000000}' WHERE Property = 'ProductCode'");
}
Also realize this cannot be done as a custom action during the installation as ProductCode is immutable. Typically in a MajorUpgrade scenario you assign a new ProductCode at build/compile time.

Grails transactions (not GORM based but using Groovy Sql)

My Grails application is not using GORM but instead uses my own SQL and DML code to read and write the database (The database is a huge normalized legacy one and this was the only viable option).
So, I use the Groovy Sql Class to do the job. The database calls are done in Services that are called in my Controllers.
Furthermore, my datasource is declared via DBCP in Tomcat - so it is not declared in Datasource.groovy.
My problem is that I need to write some transaction code, that means to open a transaction and commit after a series of successful DML calls or rollback the whole thing back in case of an error.
I thought that it would be enough to use groovy.sql.Sql#commit() and groovy.sql.Sql#rollback() respectively.
But in these methods Javadocs, the Groovy Sql documentation clearly states
If this SQL object was created from a DataSource then this method does nothing.
So, I wonder: What is the suggested way to perform transactions in my context?
Even disabling autocommit in Datasource declaration seems to be irrelevant since those two methods "...do nothing"
The Groovy Sql class has withTransaction
http://docs.groovy-lang.org/latest/html/api/groovy/sql/Sql.html#withTransaction(groovy.lang.Closure)
public void withTransaction(Closure closure)
throws java.sql.SQLException
Performs the closure within a transaction using a cached connection. If the closure takes a single argument, it will be called with the connection, otherwise it will be called with no arguments.
Give it a try.
Thanks James. I also found the following solution, reading http://grails.org/doc/latest/guide/services.html:
I declared my service as transactional
static transactional = true
This way, if an Error occurs, the previously performed DMLs will be rolled back.
For each DML statement I throw an Error describing the message. For example:
try{
sql.executeInsert("""
insert into mytable1 (col1, col2) values (${val1}, ${val2})
""")
catch(e){
throw new Error("you cant enter empty val1 or val2")
}
try{
sql.executeInsert("""
insert into mytable2 (col1, col2) values (${val1}, ${val2})
""")
catch(e){
throw new Error("you cant enter empty val1 or val2. The previous insert is rolledback!")
}
Final gotcha! The service when called from the controller, must be in a try catch, as follows:
try{
myService.myMethod(params)
}catch(e){
//http://jts-blog.com/?p=9491
Throwable t = e instanceof UndeclaredThrowableException ? e.undeclaredThrowable : e
// use t.toString() to send info to user (use in view)
// redirect / forward / render etc
}

How to save and then update same class instance during one request with NHibernate?

I'm relatively new to NHibernate and I've got a question about it.
I use this code snippet in my MVC project in Controller's method:
MyClass entity = new MyClass
{
Foo = "bar"
};
_myRepository.Save(entity);
....
entity.Foo = "bar2";
_myRepository.Save(entity);
The first time entity saved in database succesfully. But the second time not a single request doesnt go to database. My method save in repository just does:
public void Save(T entity)
{
_session.SaveOrUpdate(entity);
}
What should I do to be able to save and then update this entity during one request? If I add _session.Flush(); after saving entity to database it works, but I'm not sure, if it's the right thing to do.
Thanks
This is the expected behavior.
Changes are only saved on Flush
Flush may be called explicitly or implicitly (see 9.6. Flush)
When using an identity generator (not recommended), inserts are sent immediately, because that's the only way to return the ID.
you should be using transactions.
a couple of good sources: here and here.
also, summer of nHibernate is how I first started with nHibernate. it's a very good resource for learning the basics.