I have a string that is data bytes base64EncodedString from iOS which is an extremely long string
let imageStr = imageData.base64EncodedString()
I am calling a .NET Method from my ios that will call a stored procedure to insert these bytes into the database.
Here is my .NET Method, I have the data type set to VarBinary
public string PostLandGradingImages(List<Images> landingCells)
{
try
{
using (connection = new SqlConnection(connectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("PostLandGradingImages", connection))
{
command.CommandType = CommandType.StoredProcedure;
for (int i = 0; i < landingCells.Count; i++)
{
command.Parameters.Clear();
SqlParameter parameter1 = new SqlParameter("#Job_No", SqlDbType.VarChar);
parameter1.Value = landingCells[i].jobNo;
parameter1.Direction = ParameterDirection.Input;
command.Parameters.Add(parameter1);
SqlParameter parameter2 = new SqlParameter("#Image", SqlDbType.VarBinary);
parameter2.Value = landingCells[i].imageBytes;
parameter2.Direction = ParameterDirection.Input;
command.Parameters.Add(parameter2);
command.ExecuteNonQuery();
}
}
}
}
catch (Exception e)
{
return e.Message.ToString();
}
return "All Good";
}
Here is my Image Class, notice my imageBytes is defined as a byte[]:
public class Images
{
public string jobNo { get; set; }
public byte[] imageBytes { get; set; }
}
The column I am inserting into is defined as varbinary(MAX)
and here is my stored procedure:
ALTER PROCEDURE [dbo].[PostLandGradingImages]
-- Add the parameters for the stored procedure here
#Job_No varchar(MAX) = NULL,
#Image varbinary(MAX) = NULL
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
INSERT INTO LandGradingImages (Job_No, ImageBytes) VALUES (#Job_No, #Image)
END
My problem is nothing is getting inserted, I am getting this error in my catch:
Object reference not set to an instance of an object.
My question is, what am I doing wrong? Should I not be sending base64EncodedString or am I not setting my class right? or my db column?
I tried this:
byte[] bytes = System.Convert.FromBase64String(landingCells[i].imageBytes);
SqlParameter parameter2 = new SqlParameter("#Image", SqlDbType.VarBinary, 800000);
parameter2.Value = bytes;
parameter2.Direction = ParameterDirection.Input;
command.Parameters.Add(parameter2);
Still does not work :( and I changed imageBytes to string.
I modified your code a little to the method below. It creates a new CommandType.StoredProcedure for every Image. Also the results are returned per image, so you can see which ones failed. In your method, if you have 10 images, and the 9th failed, you would not know that.
public List<Images> PostLandGradingImages(List<Images> landingCells)
{
//create a connection to the database
using (SqlConnection connection = new SqlConnection(Common.connectionString))
{
//loop all the images
for (int i = 0; i < landingCells.Count; i++)
{
//create a fresh sql command for every Image
using (SqlCommand command = new SqlCommand("PostLandGradingImages", connection))
{
command.CommandType = CommandType.StoredProcedure;
//add the parameters
command.Parameters.Add("#Job_No", SqlDbType.VarChar).Value = landingCells[i].jobNo;
command.Parameters.Add("#Image", SqlDbType.VarBinary).Value = landingCells[i].imageBytes;
try
{
//open the connection if closed
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
//execute the stored procedure
command.ExecuteNonQuery();
//set the save result to the image
landingCells[i].saveResult = true;
}
catch (Exception ex)
{
//handle error per Image
landingCells[i].errorMessage = ex.Message;
}
}
}
}
return landingCells;
}
In order to track the save result per image I've added two properties to the Image class, but this can be done in various other ways as well.
public class Images
{
public string jobNo { get; set; }
public byte[] imageBytes { get; set; }
public bool saveResult { get; set; }
public string errorMessage { get; set; }
}
A simple test was done with the following code. None of them gave a NullReference Error. Even with both properties being null, a database entry was still made.
//create a new list with Images
List<Images> landingCells = new List<Images>();
//add some dummy data
landingCells.Add(new Images() { jobNo = null, imageBytes = null });
landingCells.Add(new Images() { jobNo = "Job 1", imageBytes = null });
landingCells.Add(new Images() { jobNo = null, imageBytes = new byte[10000] });
landingCells.Add(new Images() { jobNo = "Job 2", imageBytes = new byte[10000] });
//send the images to be saved
landingCells = PostLandGradingImages(landingCells);
//loop all the images to check the result
for (int i = 0; i < landingCells.Count; i++)
{
if (landingCells[i].saveResult == false)
{
//display the result for each failed image
Label1.Text += landingCells[i].errorMessage + "<br>";
}
}
If there is still a NullReference error, that means that your List landingCells itself is null, or an Image object within that List is null (in which case it should never have been added to the List in the first place imho). You can change the snippet easily to check for that.
Consider batching the queries in a transaction. Also you should validate the values provided to the method to make sure that you can call the stored procedure correctly.
public int PostLandGradingImages(List<Images> landingCells) {
int count = 0;
using (var connection = new SqlConnection(connectionString)) {
connection.Open();
//Transaction to batch the actions.
using (var transaction = connection.BeginTransaction()) {
foreach (var image in landingCells) {
if (valid(image)) {//validate input properties.
try {
using (SqlCommand command = connection.CreateCommand()) {
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "PostLandGradingImages";
command.Parameters
.Add("#Job_No", SqlDbType.VarChar, image.jobNo.Length)
.Value = image.jobNo;
command.Parameters
.Add("#Image", SqlDbType.VarBinary, image.imageBytes.Length)
.Value = image.imageBytes;
count += command.ExecuteNonQuery();
}
} catch {
//TODO: Log error
}
}
}
if (landingCells.Count == count) {
transaction.Commit();
}
}
}
return count;
}
private bool valid(Images image) {
return image != null && String.IsNullOrWhiteSpace(image.jobNo)
&& image.imageBytes != null && image.imageBytes.Length > 0;
}
Related
I am working on a program that upload PDF files to FileNet, and was wondering if there is a way to retrieve the GUID without running a SQL Query. I also tried Retrieving a Document (fetchInstance) from https://www.ibm.com/support/knowledgecenter/en/SSNW2F_5.5.0/com.ibm.p8.ce.dev.ce.doc/document_procedures.htm without the new Id part of the code and that did not work.
public class DocIdGenerator implements EventActionHandler {
private static final String DS = "ECMSvcsXA";
private static final String INSERT_SQL = "Insert into dbo.ICNLegalDocID_S (object_id) values (?)";
private static final String SELECT_SQL = "Select DocId From dbo.ICNLegalDocID_S Where object_id = ?";
#Override
public void onEvent(ObjectChangeEvent event, Id subscriptionId)
throws EngineRuntimeException {
// Get the Document object from the event
ObjectStore os = event.getObjectStore();
Id objectId = event.get_SourceObjectId();
PropertyFilter pf = new PropertyFilter();
pf.addIncludeProperty(new FilterElement(null, null, null, "DocumentID", null));
pf.addIncludeProperty(new FilterElement(null, null, null, PropertyNames.VERSION_SERIES, null));
Document sourceDoc = Factory.Document.fetchInstance(os, objectId, pf);
Properties props = sourceDoc.getProperties();
String documentId = props.getStringValue("DocumentID");
VersionSeries vs = sourceDoc.get_VersionSeries();
Id versionSeriesId = vs.get_Id();
if (documentId == null || documentId.isEmpty()) {
// Get the JNDI Context to lookup DataSource and Insert the objectId to get the auto generated docId
Context ctx;
DataSource ds;
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
ctx = new InitialContext();
ds = (DataSource) ctx.lookup(DS);
con = ds.getConnection();
ps = con.prepareStatement(SELECT_SQL);
ps.setString(1, versionSeriesId.toString());
rs = ps.executeQuery();
BigDecimal docId = null;
if (rs.next()) {
// Document Id already exists
docId = rs.getBigDecimal(1);
} else {
// Document Id doesn't exist inert to get it
ps = con.prepareStatement(INSERT_SQL, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, versionSeriesId.toString());
ps.executeUpdate();
rs = ps.getGeneratedKeys();
if (rs.next()) {
docId = rs.getBigDecimal(1);
}
}
props.putValue("DocumentID", docId.toString());
sourceDoc.save(RefreshMode.REFRESH);
} catch (Exception e) {
e.printStackTrace();
if (e instanceof EngineRuntimeException) {
throw (EngineRuntimeException)e;
} else {
ErrorStack es = new ErrorStack("DocIdGeneratorSub", new ErrorRecord[] {new ErrorRecord(e)});
throw new EngineRuntimeException(es);
}
} finally {
close(con, ps, rs);
}
}
}
private void close(Connection con, PreparedStatement ps, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// Ignore
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
// Ignore
}
}
if (con != null) {
try {
con.close();
} catch (SQLException e) {
// Ignore
}
}
}
}
I am not sure that I understand what to you trying to do.
When you create new document instance (Document doc = Factory.Document.newInstance()) and invoke doc.save(Refresh.REFRESH) options, FileNet inserts new DB record for document instance, generate new ID and populate it backward to the document instance on your side.
So, to get Id for new Document you just need to call doc.getId() to get it.
As I understand from your sample, you try to use event handler, If you invoke handler on the event before creation, you don't have any Id at this time, you cant try to handle the event after document creation.
I'm trying to get contact details of a contact that the user picks from the contacts list in Android using Intent as the following code:
Intent Intent = new Intent(Intent.ActionPick, ContactsContract.Contacts.ContentUri);
Intent.SetType(ContactsContract.Contacts.ContentType);
StartActivityForResult(Intent, 3);
Now on the Intent results I run the following code to get specific contact information:
public override void OnActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == 3 && resultCode == -1 && data != null) //result code -1 means OK 0 Means cancelled Result.Ok
{
var ContactData = data.Data;
string ID = "";
string name = "";
string address = "";
byte[] picture = new byte[0];
List<string> numbers = new List<string>();
List<string> emails = new List<string>();
string mobile = "";
string email = "";
string selectionString = "id = ?";
string[] columnsNames = new string[] {
ContactsContract.Contacts.InterfaceConsts.Id,
ContactsContract.Contacts.InterfaceConsts.DisplayName,
ContactsContract.Contacts.InterfaceConsts.PhotoUri
};
var loader = new CursorLoader(Statics.mainActivity, ContactData, null, null, null, null);
var cursor = (ICursor)loader.LoadInBackground();
if (cursor.MoveToFirst())
{
ID = cursor.GetString(cursor.GetColumnIndex(columnsNames[0]));
name = cursor.GetString(cursor.GetColumnIndex(columnsNames[1]));
picture = cursor.GetBlob(cursor.GetColumnIndex(columnsNames[2]));
}
//Store Contact ID
string[] selectionStringArgs = new string[] { ID };
//Phone Numbers
string[] columnsNames2 = new string[] {
ContactsContract.CommonDataKinds.Phone.Number
};
var loader2 = new CursorLoader(Statics.mainActivity, ContactsContract.CommonDataKinds.Phone.ContentUri, columnsNames2, selectionString, selectionStringArgs, null);
var cursor2 = (ICursor)loader2.LoadInBackground();
while (cursor2.MoveToNext())
{
numbers.Add(cursor2.GetString(cursor2.GetColumnIndex(columnsNames2[0])));
}
//Email Address
string[] columnsNames3 = new string[] {
ContactsContract.CommonDataKinds.Email.Address
};
var loader3 = new CursorLoader(Statics.mainActivity, ContactsContract.CommonDataKinds.Email.ContentUri, columnsNames3, selectionString, selectionStringArgs, null);
var cursor3 = (ICursor)loader3.LoadInBackground();
while (cursor3.MoveToNext())
{
emails.Add(cursor3.GetString(cursor3.GetColumnIndex(columnsNames3[0])));
}
int TempRecepitntID = 0;
EmployeesViewModel tempRecipent = new EmployeesViewModel();
TempRecepitntID = Statics.mainActivity.currentViewModel.SelectedChat.ReceiverEmployee;
foreach (EmployeesViewModel evm in Statics.mainActivity.currentViewModel.Employees)
{
if (evm.ID == TempRecepitntID)
tempRecipent = evm;
}
new Android.Support.V7.App.AlertDialog.Builder(Statics.mainActivity)
.SetPositiveButton("Yes", (sender1, args) =>
{
Statics.mainActivity.currentViewModel.AddMessage(picture, tempRecipent, Statics.mainActivity.currentViewModel.SelectedChat.ID, "contact", 0, "", name, numbers[0], mobile, email, address);
})
.SetNegativeButton("No", (sender1, args) =>
{
// cancel
})
.SetMessage("Are you shure you want to send?")
.SetTitle("System Message")
.Show();
}
}
The problem is I want to retrieve only the information of the contact that the user selected but what I get is all other contacts data is retrieved so I tried to use the selection and selectionargs parameters of CursorLoader by setting string selectionString = "id = ?"; and selectionArgs to string[] selectionStringArgs = new string[] { ID }; the ID value is retrieved from the following code :
if (cursor.MoveToFirst())
{
ID = cursor.GetString(cursor.GetColumnIndex(columnsNames[0]));
name = cursor.GetString(cursor.GetColumnIndex(columnsNames[1]));
picture = cursor.GetBlob(cursor.GetColumnIndex(columnsNames[2]));
}
//Store Contact ID
string[] selectionStringArgs = new string[] { ID };
//Phone Numbers
string[] columnsNames2 = new string[] {
ContactsContract.CommonDataKinds.Phone.Number
};
But now it returns 0 results, I couldn't find anything on the internet that applies to Xamarin android, Please help.
Thanks,
Finally I found the solution, I used the following string in the selection parameter of the cursorloader method:
string selectionString = ContactsContract.CommonDataKinds.Phone.InterfaceConsts.ContactId + "=" + ID;
and now only the selected contact numbers are retrieved.
I hope this will help someone else.
In additional information of #TMSL, I add the code afer this bloque
if (cursor.MoveToFirst())
{
ID = cursor.GetString(cursor.GetColumnIndex(columnsNames[0]));
name = cursor.GetString(cursor.GetColumnIndex(columnsNames[1]));
picture = cursor.GetBlob(cursor.GetColumnIndex(columnsNames[2]));
}
Here
selectionString = ContactsContract.CommonDataKinds.Phone.InterfaceConsts.ContactId + "=" + ID;
Then I changed the parameters used in the definition of variable Loader2, converting selectionStringArgs in null.
var loader2 = new CursorLoader(this.Activity, ContactsContract.CommonDataKinds.Phone.ContentUri, columnsNames2, selectionString, null,null);
var cursor2 = (ICursor)loader2.LoadInBackground();
I found this documentation from xamarin guides
Uri – The fully qualified name of the ContentProvider.
Projection – Specification of which columns to select for the cursor.
Selection – Similar to a SQL WHERE clause.
SelectionArgs – Parameters to be substituted in the Selection.
SortOrder – Columns to sort by.
So, the variable selectionStringArgs used in the code from #TMSAL cannot use a value like "contact_id = 2700", because the parameter of CursorLoader SelectionArgs is not a filter but not "Parameters to be substituted in the Selection"
I hope this will help someone else too.
I've got a complex form on a page that is bound to a POCO representing a rather complex entity. One of the requirements is that, on blur, I update the database.
I'm currently passing the property (as key), value, and CampaignId via ajax. The key might look something like: Campaign.FanSettings.SocialSharing.FacebookLinkText.
I am using the code below, and getting "close". My final propertyToSet is the FacebookLinkText is not being set, because my object source is of type Entities.Campaign, while my object value is simply a string. I understand these need to be the same type, but I don't understand how to do that. Two questions:
How do I modify the code below to be able to execute the propertyToSet.SetValue method
Since I'm casting this to an object, I don't see how this would actually update my entity, so when I call SaveChanges it updates appropriately. What am I missing?
Thanks!
Code:
public void UpdateCampaign(int id, string key, string value)
{
using (var context = new BetaEntities())
{
var camp = context.Campaigns.Where(e => e.Id == id).Single();
SetProperty(camp, key,value);
}
}
public void SetProperty(object source, string property, object value)
{
string[] bits = property.Split('.');
for (int i = 0; i < bits.Length - 1; i++)
{
PropertyInfo prop = source.GetType().GetProperty(bits[i]);
source = prop.GetValue(source, null);
}
PropertyInfo propertyToSet = null;
if (source is IEnumerable)
{
foreach (object o in (source as IEnumerable))
{
propertyToSet = o.GetType().GetProperty(bits[bits.Length - 1]);
break;
}
}
else
{
propertyToSet = source.GetType().GetProperty(bits[bits.Length - 1]);
}
propertyToSet.SetValue(source, value, null);
}
Solved.
public void UpdateCampaign(int id, string key, string value)
{
using (var context = new BetaEntities())
{
var camp = context.Campaigns.Where(e => e.Id == id).Single();
SetProperty(camp, key, value);
context.SaveChanges()
}
}
public void SetProperty(object source, string property, object value)
{
string[] bits = property.Split('.');
for (int i = 0; i < bits.Length - 1; i++)
{
PropertyInfo prop = source.GetType().GetProperty(bits[i]);
source = prop.GetValue(source, null);
}
PropertyInfo propertyToSet = null;
if (source is IEnumerable)
{
foreach (object o in (source as IEnumerable))
{
propertyToSet = o.GetType().GetProperty(bits[bits.Length - 1]);
propertyToSet.SetValue(o, value,null);
break;
}
}
else
{
propertyToSet = source.GetType().GetProperty(bits[bits.Length - 1]);
propertyToSet.SetValue(source, value, null);
}
}
I am using this code to retrieve the receipient name and receipient number but the recpt.receipient_name and recpt.receipient_number are null.
The excel table is of this format
Name Number
andrew 1223
james 12223
dave 454545
//select names from the excel file with specified sheet name
var receipients = from n in messages.Worksheet<BulkSmsModel>(sheetName)
select n;
foreach (var recpt in receipients)
{
BulkSms newRecpt = new BulkSms();
if (recpt.receipient_number.Equals("") == true || recpt.receipient_number == 0)
{
continue;
}
newRecpt.receipient_name = recpt.receipient_name;
newRecpt.receipient_number = Int32.Parse(recpt.receipient_number.ToString());
IBsmsRepo.insertReceipient(newRecpt);
IBsmsRepo.save();
}
After some research I found a way to get the value from excel file with LinqToExcel and get the list of all Cells. Check this MVC C# Sample.
using LinqToExcel;
using Syncfusion.Olap.Reports;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.OleDb;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace YourProject.Controllers
{
public class DefaultController : Controller
{
// GET: Default1
public ActionResult Index()
{
return View();
}
public dynamic UploadExcel(HttpPostedFileBase FileUpload)
{
string PathToyurDirectory = ConfigurationManager.AppSettings["Path"].ToString();//This can be in Anywhere, but you have to create a variable in WebConfig AppSettings like this <add key="Path" value="~/Doc/"/> This directory in my case is inside App whereI upload the files here, and I Delete it after use it ;
if (FileUpload.ContentType == "application/vnd.ms-excel"
|| FileUpload.ContentType == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|| FileUpload.ContentType == "application/vnd.ms-excel.sheet.binary.macroEnabled.12"
)
{
string filename = FileUpload.FileName;
string PathToExcelFile = Server.MapPath(PathToyurDirectory + filename);
// string targetpath = ;
FileUpload.SaveAs(PathToyurDirectory);
var connectionString = string.Empty;
string sheetName = string.Empty;
yourmodel db = new yourmodel();
Employee Employee = New Employee(); //This is your class no matter What.
try
{
if (filename.EndsWith(".xls") || filename.EndsWith(".csv") || filename.EndsWith(".xlsx") || filename.EndsWith(".xlsb"))
{
connectionString = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0 Xml;HDR=YES;IMEX=1\";", PathToExcelFile);
sheetName = GetTableName(connectionString);
}
var ExcelFile = new ExcelQueryFactory(PathToExcelFile);
var Data = ExcelFile.Worksheet(sheetName).ToList();
foreach (var item in Data)
{
//if yout excel file does not meet the class estructure.
Employee = new Employee
{
Name = item[1].ToString(),
LastName = item[2].ToString(),
Address = item[3].ToString(),
Phone = item[4].ToString(),
CelPghone = item[5].ToString()
};
db.Employee.Add(Employee);
db.SaveChanges();
}
}
catch (Exception)
{
throw;
}
}
return View();
}
private string GetTableName(string connectionString)
{
// You can return all Sheets for a Dropdown if you want to, for me, I just want the first one;
OleDbConnection oledbconn = new OleDbConnection(connectionString);
oledbconn.Open();
// Get the data table containg the schema guid.
var dt = oledbconn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
var sheet = dt.Rows[0]["TABLE_NAME"].ToString().Replace("$", string.Empty);
oledbconn.Close();
return sheet;
}
}
}
Since the property names on the BulkSmsModel class do not correlate directly to the column names in the spreadsheet, you will need to map the property names to the column names.
Assuming messages is the ExcelQueryFactory object, this would be the code.
var messages = new ExcelQueryFactory("excelFileName");
messages.AddMapping<BulkSmsModel>(x => x.receipient_name, "Name");
messages.AddMapping<BulkSmsModel>(x => x.receipient_number, "Number");
var receipients = from n in messages.Worksheet<BulkSmsModel>(sheetName)
select n;
The code below shows that a record is deleted when the sql statement is:
select * from test where qty between 50 and 59
but the sql statement:
select * from test where partno like 'PART/005%'
throws the exception:
Advantage.Data.Provider.AdsException: Error 5072: Action requires read-write access to the table
How can you reliably delete a record with a where clause applied?
Note: I'm using Advantage Database v9.10.1.9, VS2008, .Net Framework 3.5 and WinXP 32 bit
using System.IO;
using Advantage.Data.Provider;
using AdvantageClientEngine;
using NUnit.Framework;
namespace NetworkEidetics.Core.Tests.Dbf
{
[TestFixture]
public class AdvantageDatabaseTests
{
private const string DefaultConnectionString = #"data source={0};ServerType=local;TableType=ADS_CDX;LockMode=COMPATIBLE;TrimTrailingSpaces=TRUE;ShowDeleted=FALSE";
private const string TestFilesDirectory = "./TestFiles";
[SetUp]
public void Setup()
{
const string createSql = #"CREATE TABLE [{0}] (ITEM_NO char(4), PARTNO char(20), QTY numeric(6,0), QUOTE numeric(12,4)) ";
const string insertSql = #"INSERT INTO [{0}] (ITEM_NO, PARTNO, QTY, QUOTE) VALUES('{1}', '{2}', {3}, {4})";
const string filename = "test.dbf";
var connectionString = string.Format(DefaultConnectionString, TestFilesDirectory);
using (var connection = new AdsConnection(connectionString)) {
connection.Open();
using (var transaction = connection.BeginTransaction()) {
using (var command = connection.CreateCommand()) {
command.CommandText = string.Format(createSql, filename);
command.Transaction = transaction;
command.ExecuteNonQuery();
}
transaction.Commit();
}
using (var transaction = connection.BeginTransaction()) {
for (var i = 0; i < 1000; ++i) {
using (var command = connection.CreateCommand()) {
var itemNo = string.Format("{0}", i);
var partNumber = string.Format("PART/{0:d4}", i);
var quantity = i;
var quote = i * 10;
command.CommandText = string.Format(insertSql, filename, itemNo, partNumber, quantity, quote);
command.Transaction = transaction;
command.ExecuteNonQuery();
}
}
transaction.Commit();
}
connection.Close();
}
}
[TearDown]
public void TearDown()
{
File.Delete("./TestFiles/test.dbf");
}
[Test]
public void CanDeleteRecord()
{
const string sqlStatement = #"select * from test";
Assert.AreEqual(1000, GetRecordCount(sqlStatement));
DeleteRecord(sqlStatement, 3);
Assert.AreEqual(999, GetRecordCount(sqlStatement));
}
[Test]
public void CanDeleteRecordBetween()
{
const string sqlStatement = #"select * from test where qty between 50 and 59";
Assert.AreEqual(10, GetRecordCount(sqlStatement));
DeleteRecord(sqlStatement, 3);
Assert.AreEqual(9, GetRecordCount(sqlStatement));
}
[Test]
public void CanDeleteRecordWithLike()
{
const string sqlStatement = #"select * from test where partno like 'PART/005%'";
Assert.AreEqual(10, GetRecordCount(sqlStatement));
DeleteRecord(sqlStatement, 3);
Assert.AreEqual(9, GetRecordCount(sqlStatement));
}
public int GetRecordCount(string sqlStatement)
{
var connectionString = string.Format(DefaultConnectionString, TestFilesDirectory);
using (var connection = new AdsConnection(connectionString)) {
connection.Open();
using (var command = connection.CreateCommand()) {
command.CommandText = sqlStatement;
var reader = command.ExecuteExtendedReader();
return reader.GetRecordCount(AdsExtendedReader.FilterOption.RespectFilters);
}
}
}
public void DeleteRecord(string sqlStatement, int rowIndex)
{
var connectionString = string.Format(DefaultConnectionString, TestFilesDirectory);
using (var connection = new AdsConnection(connectionString)) {
connection.Open();
using (var command = connection.CreateCommand()) {
command.CommandText = sqlStatement;
var reader = command.ExecuteExtendedReader();
reader.GotoBOF();
reader.Read();
if (rowIndex != 0) {
ACE.AdsSkip(reader.AdsActiveHandle, rowIndex);
}
reader.DeleteRecord();
}
connection.Close();
}
}
}
}
LIKE results in a static cursor instead of a live cursor, meaning it is a read-only dataset. To remove a row in this situation it would be better to use an SQL DELETE statement.
DELETE FROM test where partno LIKE 'PART/005%'
I'm assuming your tests are just that, only tests. They are using some fairly inefficient mechanisms to locate and remove rows.
Update after comment that there is no key field:
How about using the LEFT scalar instead of LIKE (might not work for all cases, but does for your example). If the size is always the same you could also add an index on left(partno,8) to increase the performance:
select * from test where left(partno,8) = 'PART/005'
Then you could use the Delete function of the extended data reader directly on this live result set (no gotop and skip).
Update after Alex's ROWID comment
I didn't know our ROWID came from the base table, even in static cursors. Alex's comment is the solution to your problem. First:
SELECT t.*, t.rowid FROM test t WHERE x LIKE 'PART/005%'
then:
DELETE FROM test WHERE rowid = :thisid