(this.tables);
}
}
///
/// Gets the set of columns that were included in the query for this View,
/// or null if this view is not a SELECT query.
///
/// the View is not in an active state
/// the View handle is invalid
///
/// Win32 MSI API:
/// MsiViewGetColumnInfo
///
public ColumnCollection Columns
{
get
{
if (this.columns == null)
{
this.columns = new ColumnCollection(this);
}
return this.columns;
}
}
///
/// Executes a SQL View query and supplies any required parameters. The query uses the
/// question mark token to represent parameters as described in SQL Syntax. The values of
/// these parameters are passed in as the corresponding fields of a parameter record.
///
/// Optional Record that supplies the parameters. This
/// Record contains values to replace the parameter tokens in the SQL query.
/// the View could not be executed
/// the View handle is invalid
///
/// Win32 MSI API:
/// MsiViewExecute
///
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Params"), SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public void Execute(Record executeParams)
{
uint ret = RemotableNativeMethods.MsiViewExecute(
(int) this.Handle,
(executeParams != null ? (int) executeParams.Handle : 0));
if (ret == (uint) NativeMethods.Error.BAD_QUERY_SYNTAX)
{
throw new BadQuerySyntaxException(this.sql);
}
else if (ret != 0)
{
throw InstallerException.ExceptionFromReturnCode(ret);
}
}
///
/// Executes a SQL View query.
///
/// the View could not be executed
/// the View handle is invalid
///
/// Win32 MSI API:
/// MsiViewExecute
///
public void Execute() { this.Execute(null); }
///
/// Fetches the next sequential record from the view, or null if there are no more records.
///
/// the View is not in an active state
/// the View handle is invalid
///
/// The Record object should be d after use.
/// It is best that the handle be closed manually as soon as it is no longer
/// needed, as leaving lots of unused handles open can degrade performance.
///
/// Win32 MSI API:
/// MsiViewFetch
///
public Record Fetch()
{
int recordHandle;
uint ret = RemotableNativeMethods.MsiViewFetch((int) this.Handle, out recordHandle);
if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS)
{
return null;
}
else if (ret != 0)
{
throw InstallerException.ExceptionFromReturnCode(ret);
}
Record r = new Record((IntPtr) recordHandle, true, this);
r.IsFormatStringInvalid = true;
return r;
}
///
/// Updates a fetched Record.
///
/// specifies the modify mode
/// the Record to modify
/// the modification failed,
/// or a validation was requested and the data did not pass
/// the View handle is invalid
///
/// You can update or delete a record immediately after inserting, or seeking provided you
/// have NOT modified the 0th field of the inserted or sought record.
///
/// To execute any SQL statement, a View must be created. However, a View that does not
/// create a result set, such as CREATE TABLE, or INSERT INTO, cannot be used with any of
/// the Modify methods to update tables though the view.
///
/// You cannot fetch a record containing binary data from one database and then use
/// that record to insert the data into another database. To move binary data from one database
/// to another, you should export the data to a file and then import it into the new database
/// using a query and the . This ensures that each database has
/// its own copy of the binary data.
///
/// Note that custom actions can only add, modify, or remove temporary rows, columns,
/// or tables from a database. Custom actions cannot modify persistent data in a database,
/// such as data that is a part of the database stored on disk.
///
/// Win32 MSI API:
/// MsiViewModify
///
///
///
///
///
///
///
///
///
///
///
///
///
///
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public void Modify(ViewModifyMode mode, Record record)
{
if (record == null)
{
throw new ArgumentNullException("record");
}
uint ret = RemotableNativeMethods.MsiViewModify((int) this.Handle, (int) mode, (int) record.Handle);
if (mode == ViewModifyMode.Insert || mode == ViewModifyMode.InsertTemporary)
{
record.IsFormatStringInvalid = true;
}
if (ret != 0)
{
throw InstallerException.ExceptionFromReturnCode(ret);
}
}
///
/// Refreshes the data in a Record.
///
/// the Record to be refreshed
/// the refresh failed
/// the View handle is invalid
///
/// The Record must have been obtained by calling . Fails with
/// a deleted Record. Works only with read-write Records.
///
/// See for more remarks.
///
/// Win32 MSI API:
/// MsiViewModify
///
public void Refresh(Record record) { this.Modify(ViewModifyMode.Refresh, record); }
///
/// Inserts a Record into the view.
///
/// the Record to be inserted
/// the insertion failed
/// the View handle is invalid
///
/// Fails if a row with the same primary keys exists. Fails with a read-only database.
/// This method cannot be used with a View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI API:
/// MsiViewModify
///
public void Insert(Record record) { this.Modify(ViewModifyMode.Insert, record); }
///
/// Updates the View with new data from the Record.
///
/// the new data
/// the update failed
/// the View handle is invalid
///
/// Only non-primary keys can be updated. The Record must have been obtained by calling
/// . Fails with a deleted Record. Works only with read-write Records.
///
/// See for more remarks.
///
/// Win32 MSI API:
/// MsiViewModify
///
public void Update(Record record) { this.Modify(ViewModifyMode.Update, record); }
///
/// Updates or inserts a Record into the View.
///
/// the Record to be assigned
/// the assignment failed
/// the View handle is invalid
///
/// Updates record if the primary keys match an existing row and inserts if they do not match.
/// Fails with a read-only database. This method cannot be used with a View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI API:
/// MsiViewModify
///
public void Assign(Record record) { this.Modify(ViewModifyMode.Assign, record); }
///
/// Updates or deletes and inserts a Record into the View.
///
/// the Record to be replaced
/// the replacement failed
/// the View handle is invalid
///
/// The Record must have been obtained by calling . Updates record if the
/// primary keys are unchanged. Deletes old row and inserts new if primary keys have changed.
/// Fails with a read-only database. This method cannot be used with a View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI API:
/// MsiViewModify
///
public void Replace(Record record) { this.Modify(ViewModifyMode.Replace, record); }
///
/// Deletes a Record from the View.
///
/// the Record to be deleted
/// the deletion failed
/// the View handle is invalid
///
/// The Record must have been obtained by calling . Fails if the row has been
/// deleted. Works only with read-write records. This method cannot be used with a View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI API:
/// MsiViewModify
///
public void Delete(Record record) { this.Modify(ViewModifyMode.Delete, record); }
///
/// Inserts a Record into the View. The inserted data is not persistent.
///
/// the Record to be inserted
/// the insertion failed
/// the View handle is invalid
///
/// Fails if a row with the same primary key exists. Works only with read-write records.
/// This method cannot be used with a View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI API:
/// MsiViewModify
///
public void InsertTemporary(Record record) { this.Modify(ViewModifyMode.InsertTemporary, record); }
///
/// Refreshes the information in the supplied record without changing the position
/// in the result set and without affecting subsequent fetch operations.
///
/// the Record to be filled with the result of the seek
/// the seek failed
/// the View handle is invalid
///
/// After seeking, the Record may then be used for subsequent Update, Delete, and Refresh
/// operations. All primary key columns of the table must be in the query and the Record must
/// have at least as many fields as the query. Seek cannot be used with multi-table queries.
/// This method cannot be used with a View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI API:
/// MsiViewModify
///
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public bool Seek(Record record)
{
if (record == null)
{
throw new ArgumentNullException("record");
}
uint ret = RemotableNativeMethods.MsiViewModify((int) this.Handle, (int) ViewModifyMode.Seek, (int) record.Handle);
record.IsFormatStringInvalid = true;
if (ret == (uint) NativeMethods.Error.FUNCTION_FAILED)
{
return false;
}
else if (ret != 0)
{
throw InstallerException.ExceptionFromReturnCode(ret);
}
return true;
}
///
/// Inserts or validates a record.
///
/// the Record to be merged
/// true if the record was inserted or validated, false if there is an existing
/// record with the same primary keys that is not identical
/// the merge failed (for a reason other than invalid data)
/// the View handle is invalid
///
/// Works only with read-write records. This method cannot be used with a
/// View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI API:
/// MsiViewModify
///
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters")]
public bool Merge(Record record)
{
if (record == null)
{
throw new ArgumentNullException("record");
}
uint ret = RemotableNativeMethods.MsiViewModify((int) this.Handle, (int) ViewModifyMode.Merge, (int) record.Handle);
if (ret == (uint) NativeMethods.Error.FUNCTION_FAILED)
{
return false;
}
else if (ret != 0)
{
throw InstallerException.ExceptionFromReturnCode(ret);
}
return true;
}
///
/// Validates a record, returning information about any errors.
///
/// the Record to be validated
/// null if the record was validated; if there is an existing record with
/// the same primary keys that has conflicting data then error information is returned
/// the validation failed (for a reason other than invalid data)
/// the View handle is invalid
///
/// The Record must have been obtained by calling .
/// Works with read-write and read-only records. This method cannot be used
/// with a View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI APIs:
/// MsiViewModify,
/// MsiViewGetError
///
public ICollection Validate(Record record) { return this.InternalValidate(ViewModifyMode.Validate, record); }
///
/// Validates a new record, returning information about any errors.
///
/// the Record to be validated
/// null if the record was validated; if there is an existing
/// record with the same primary keys then error information is returned
/// the validation failed (for a reason other than invalid data)
/// the View handle is invalid
///
/// Checks for duplicate keys. The Record must have been obtained by
/// calling . Works with read-write and read-only records.
/// This method cannot be used with a View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI APIs:
/// MsiViewModify,
/// MsiViewGetError
///
[SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
public ICollection ValidateNew(Record record) { return this.InternalValidate(ViewModifyMode.ValidateNew, record); }
///
/// Validates fields of a fetched or new record, returning information about any errors.
/// Can validate one or more fields of an incomplete record.
///
/// the Record to be validated
/// null if the record was validated; if there is an existing record with
/// the same primary keys that has conflicting data then error information is returned
/// the validation failed (for a reason other than invalid data)
/// the View handle is invalid
///
/// Works with read-write and read-only records. This method cannot be used with
/// a View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI APIs:
/// MsiViewModify,
/// MsiViewGetError
///
public ICollection ValidateFields(Record record) { return this.InternalValidate(ViewModifyMode.ValidateField, record); }
///
/// Validates a record that will be deleted later, returning information about any errors.
///
/// the Record to be validated
/// null if the record is safe to delete; if another row refers to
/// the primary keys of this row then error information is returned
/// the validation failed (for a reason other than invalid data)
/// the View handle is invalid
///
/// Validation does not check for the existence of the primary keys of this row in properties
/// or strings. Does not check if a column is a foreign key to multiple tables. Works with
/// read-write and read-only records. This method cannot be used with a View containing joins.
///
/// See for more remarks.
///
/// Win32 MSI APIs:
/// MsiViewModify,
/// MsiViewGetError
///
public ICollection ValidateDelete(Record record) { return this.InternalValidate(ViewModifyMode.ValidateDelete, record); }
///
/// Enumerates over the Records retrieved by the View.
///
/// An enumerator of Record objects.
/// The View was not d before attempting the enumeration.
///
/// Each Record object should be d after use.
/// It is best that the handle be closed manually as soon as it is no longer
/// needed, as leaving lots of unused handles open can degrade performance.
/// However, note that it is not necessary to complete the enumeration just
/// for the purpose of closing handles, because Records are fetched lazily
/// on each step of the enumeration.
///
/// Win32 MSI API:
/// MsiViewFetch
///
public IEnumerator GetEnumerator()
{
Record rec;
while ((rec = this.Fetch()) != null)
{
yield return rec;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private ICollection InternalValidate(ViewModifyMode mode, Record record)
{
uint ret = RemotableNativeMethods.MsiViewModify((int) this.Handle, (int) mode, (int) record.Handle);
if (ret == (uint) NativeMethods.Error.INVALID_DATA)
{
ICollection errorInfo = new List();
while (true)
{
uint bufSize = 40;
StringBuilder column = new StringBuilder("", (int) bufSize);
int error = RemotableNativeMethods.MsiViewGetError((int) this.Handle, column, ref bufSize);
if (error == -2 /*MSIDBERROR_MOREDATA*/)
{
column.Capacity = (int) ++bufSize;
error = RemotableNativeMethods.MsiViewGetError((int) this.Handle, column, ref bufSize);
}
if (error == -3 /*MSIDBERROR_INVALIDARG*/)
{
throw InstallerException.ExceptionFromReturnCode((uint) NativeMethods.Error.INVALID_PARAMETER);
}
else if (error == 0 /*MSIDBERROR_NOERROR*/)
{
break;
}
errorInfo.Add(new ValidationErrorInfo((ValidationError) error, column.ToString()));
}
return errorInfo;
}
else if (ret != 0)
{
throw InstallerException.ExceptionFromReturnCode(ret);
}
return null;
}
}
}