Hi I am developing one bot using Microsoft botframework project in that I am using IDialog interface. In that I am using the ThumbnailCard for displaying the cards. Here when I am attaching some data to my cards and the data is attaching properly but within the PostAsync method it’s not providing the reply.
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
ThumbnailCard plCard = null;
IMessageActivity replyToConversation =await argument;
replyToConversation.Type = "message";
replyToConversation.Attachments = new List<Attachment>();
replyToConversation.Text = "welcome to book my show";
Dictionary<string, string> cardContentList = new Dictionary<string, string>();
cardContentList.Add("Jason Bourne", "URL");
cardContentList.Add("The Land", "URL");
cardContentList.Add("Yoga Hosers", "URL");
foreach (KeyValuePair<string, string> cardContent in cardContentList)
{
List<CardImage> cardImages = new List<CardImage>();
cardImages.Add(new CardImage(url: cardContent.Value));
List<CardAction> cardButtons = new List<CardAction>();
if (cardContent.Key == "Jason Bourne")
{
CardAction plButton1 = new CardAction()
{
Value = $"",
Type = "openUrl",
Title = "Book Now"
};
CardAction plButton2 = new CardAction()
{
Value = "tel:1-800-800-5705",
Type = "call",
Title = "Show timings"
};
cardButtons.Add(plButton1);
cardButtons.Add(plButton2);
plCard = new ThumbnailCard()
{
Title = $"Jason Bourne",
Subtitle = " ",
Images = cardImages,
Buttons = cardButtons,
};
Attachment plAttachment = plCard.ToAttachment();
replyToConversation.Attachments.Add(plAttachment);
}
else if (cardContent.Key == "The Land")
{
CardAction plButton1 = new CardAction()
{
Value = $"",
Type = "openUrl",
Title = "Book Now"
};
CardAction plButton2 = new CardAction()
{
Value = "tel:1-800-800-5705",
Type = "call",
Title = "Show Timings"
};
cardButtons.Add(plButton1);
cardButtons.Add(plButton2);
plCard = new ThumbnailCard()
{
Title = $"The Land",
Subtitle = "",
Images = cardImages,
Buttons = cardButtons,
};
Attachment plAttachment = plCard.ToAttachment();
replyToConversation.Attachments.Add(plAttachment);
}
else if (cardContent.Key == "Yoga Hosers")
{
CardAction plButton1 = new CardAction()
{
Value = $"",
Type = "openUrl",
Title = "Book Now"
};
CardAction plButton2 = new CardAction()
{
Value = "tel:1-800-800-5705",
Type = "call",
Title = "Show timings"
};
cardButtons.Add(plButton1);
cardButtons.Add(plButton2);
plCard = new ThumbnailCard()
{
Title = $"Yoga Hosers",
Subtitle = "",
Images = cardImages,
Buttons = cardButtons,
};
Attachment plAttachment = plCard.ToAttachment();
replyToConversation.Attachments.Add(plAttachment);
}
}
replyToConversation.AttachmentLayout = AttachmentLayoutTypes.List;
await context.PostAsync(replyToConversation);
}
When I run the bot its show the following error
Can we use cards in IDialog Context for attachments?
The issue is with the IMessageActivity, you are trying to send IMessageActicity in context.PostAsync. That's the reason it is failing.
Do the following changes to make it work
Change the method signature like below
private async Task messageReceived(IDialogContext context, IAwaitable<object> argument)
and modify the IMessageActivity replyToConversation =await argument; to like below
var message = await argument as Activity;
Activity replyToConversation = message.CreateReply("Welcome." + "(Hi)");
replyToConversation.Recipient = message.From;
Now it should work, if you still have issue please comment here.
-Kishore
Related
I am looking for help, I hope I can solve this problem.
I've created a new screen and I'm filtering all the Revalue Accounts information.
With this screen, what I want to do is insert and at the same time add an additional new line within the grid.
Here is my new created screen and the button that inserts GLTran
I am attaching an image where I want the line to be added at insert time
Here I share code that I have and it does not work for the additional line.
private void CreateDNGL(Batch batch)
{
var graph = PXGraph.CreateInstance<JournalEntry>();
if (graph.BatchModule.Current == null)
{
Batch cmbatch = new Batch();
cmbatch.BranchID = batch.BranchID;
cmbatch.Module = batch.Module;
cmbatch.Status = "U";
cmbatch.AutoReverse = true;
cmbatch.Released = true;
cmbatch.Hold = false;
cmbatch.CuryDebitTotal = batch.CuryDebitTotal;
cmbatch.CuryCreditTotal = batch.CuryCreditTotal;
cmbatch.FinPeriodID = batch.FinPeriodID;
cmbatch.CuryID = batch.CuryID;
cmbatch.CuryInfoID = batch.CuryInfoID;
cmbatch.DebitTotal = batch.DebitTotal;
cmbatch.CreditTotal = batch.CreditTotal;
cmbatch.Description = "Head new insert";
cmbatch = graph.BatchModule.Insert(cmbatch);
}
foreach (GLTran item in PXSelect<GLTran,
Where<GLTran.module, Equal<Required<GLTran.module>>,
And<GLTran.batchNbr, Equal<Required<GLTran.batchNbr>>>>>.Select(this, batch.Module, batch.BatchNbr))
{
GLTran tran = new GLTran();
tran.SummPost = item.SummPost;
tran.ZeroPost = false;
tran.DebitAmt = item.DebitAmt;
tran.CreditAmt = item.CreditAmt;
tran.CuryDebitAmt = item.CuryDebitAmt;
tran.CuryCreditAmt = item.CuryCreditAmt;
tran.AccountID = item.AccountID;
tran.SubID = item.SubID;
tran.LineNbr = item.LineNbr;
tran.LedgerID = item.LedgerID;
tran.TranType = item.TranType;
tran.TranClass = item.TranClass;
tran.RefNbr = string.Empty;
tran.FinPeriodID = item.FinPeriodID;
tran.TranDesc = "Test detail";
tran.Released = true;
tran.ReferenceID = item.ReferenceID;
tran = graph.GLTranModuleBatNbr.Insert(tran);
Account account = PXSelect<Account, Where<Account.accountID,
Equal<Required<Account.accountID>>>>.Select(graph, item.AccountID);
xLocEquivalAcct equivalAcct = PXSelect<xLocEquivalAcct, Where<xLocEquivalAcct.acctCD,
Equal<Required<xLocEquivalAcct.acctCD>>>>.Select(graph, account.AccountCD);
if (equivalAcct != null)
{
/*here is added for an additional line*/
var glTran = graph.GLTranModuleBatNbr.Insert();
graph.GLTranModuleBatNbr.SetValueExt<GLTran.accountID>(glTran, 343567);
graph.GLTranModuleBatNbr.SetValueExt<GLTran.subID>(glTran, 281);
glTran.TranDesc = "add extra line";
if (item.DebitAmt != 0m && item.CreditAmt == 0m)
{
if (batch.Module == BatchModule.CM)
{
graph.GLTranModuleBatNbr.SetValueExt<GLTran.curyDebitAmt>(glTran, item.CuryDebitAmt);
graph.GLTranModuleBatNbr.SetValueExt<GLTran.debitAmt>(glTran, item.DebitAmt);
}
}
if (item.CreditAmt != 0m && item.DebitAmt == 0m)
{
if (batch.Module == BatchModule.CM)
{
graph.GLTranModuleBatNbr.SetValueExt<GLTran.curyCreditAmt>(glTran, item.CuryCreditAmt);
graph.GLTranModuleBatNbr.SetValueExt<GLTran.creditAmt>(glTran, item.CreditAmt);
}
}
glTran = graph.GLTranModuleBatNbr.Update(glTran);
}
}
graph.Save.Press();
}
I hope I was clear with my question.
This code will create a copy of the originating batch and insert additional lines.
#Warning Your original code was attempting to create a batch that was already released but unposted. I can update my answer to match your requirement but this will break Acumatica work-flow.
Please find code example below :
public class JournalEntryExtension : PXGraphExtension<JournalEntry>
{
public PXAction<Batch> CopyCreate;
//CommitChanges being set to false allows the graph to not be considered dirty when there are errors that we manually show on screen. Case #207998
[PXButton(CommitChanges = false)]
[PXUIField(DisplayName = "Copy Create", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update, Enabled = true)]
protected virtual IEnumerable copyCreate(PXAdapter pxAdapter)
{
if (Base.BatchModule.Current != null)
{
if (Base.IsDirty)
{
Base.BatchModule.Ask("You must discard your unsaved changes to be able to press this button.", MessageButtons.OK);
}
else
{
Batch batch = Base.BatchModule.Current;
PXLongOperation.StartOperation(this, () => CreateDNGL(batch));
}
}
return pxAdapter.Get();
}
private static void CreateDNGL(Batch batch)
{
JournalEntry graph = PXGraph.CreateInstance<JournalEntry>();
Batch newBatch = PXCache<Batch>.CreateCopy(batch);
newBatch.NoteID = null;
newBatch.Description = "Test header";
newBatch = graph.BatchModule.Insert(newBatch);
GLTran newTran;
foreach (GLTran tran in PXSelectReadonly<GLTran,
Where<GLTran.module, Equal<Required<GLTran.module>>,
And<GLTran.batchNbr, Equal<Required<GLTran.batchNbr>>>>>.Select(graph, batch.Module, batch.BatchNbr))
{
newTran = PXCache<GLTran>.CreateCopy(tran);
newTran.Module = null;
newTran.BatchNbr = null;
newTran.NoteID = null;
newTran.TranDesc = "Test detail";
newTran = graph.GLTranModuleBatNbr.Insert(newTran);
}
if (true)
{
newTran = graph.GLTranModuleBatNbr.Insert();
newTran.AccountID = 1190;
newTran.SubID = 467;
newTran.CuryDebitAmt = 1000;
newTran.TranDesc = "Additional Line 1";
newTran = graph.GLTranModuleBatNbr.Update(newTran);
newTran = graph.GLTranModuleBatNbr.Insert();
newTran.AccountID = 1190;
newTran.SubID = 467;
newTran.CuryCreditAmt = 1000;
newTran.TranDesc = "Additional Line 2";
newTran = graph.GLTranModuleBatNbr.Update(newTran);
}
graph.Save.Press();
}
}
Original Batch :
New Batch :
Good day
I am creating a SOAP contract base connection to Acumatica.
I am getting an error: "System.ArgumentNullException: Value cannot be null."
I am not sure why I am getting the error.
Here is my code
using (var soapClient = new DefaultSoapClient())
{
try
{
soapClient.Login();
InventoryReceipt NewinventoryReceipt = new InventoryReceipt
{
ReferenceNbr = new StringValue { Value = "<NEW>" },
Hold = new BooleanValue { Value = true },
Date = new DateTimeValue { Value = DateTime.Now },
PostPeriod = new StringValue { Value = DateTime.Now.ToString("DD-yyyy") },
TransferNbr = new StringValue { Value = "" },
//External Ref
Description = new StringValue { Value = "" },
Details = new InventoryReceiptDetail[]
{
new InventoryReceiptDetail
{
//branch
InventoryID = new StringValue{Value = "NIS777"},
WarehouseID = new StringValue{Value = "FBTZEST"},
Location = new StringValue {Value = "BULK"},
Qty = new DecimalValue{Value = 1},
UOM = new StringValue{Value = "PALLET"},
UnitCost = new DecimalValue{Value = 91},
ExtCost = new DecimalValue{Value = 91},
LotSerialNbr = new StringValue{Value = "PLN12345"},
ExpirationDate = new DateTimeValue{Value = DateTime.Now},
// ReasonCode
Description = new StringValue{Value = ""}
}
},
};
InventoryReceipt putInventoryReceipt = (InventoryReceipt)soapClient.Put(NewinventoryReceipt);
}
catch (Exception ex)
{
soapClient.Logout();
throw;
}
finally
{
soapClient.Logout();
}
soapClient.Logout();
}
Console.ReadLine();
}
Is there any way to see what is null or what I am missing to post this data?
Have you tried manually entering the data into the UI? The Validation on the web service should be the same as the UI, so you might get more info from the UI. You have a lot of dependent values here since you're referencing a specific Lot perhaps a value is missing. Other than that, you might try adding Project = X.
I'm using the SuiteTalk (API) service for NetSuite to retrieve a list of Assemblies. I need to load the InventoryDetails fields on the results to view the serial/lot numbers assigned to the items. This is the current code that I'm using, but the results still show those fields to come back as NULL, although I can see the other fields for the AssemblyBuild object. How do I get the inventory details (serials/lot#'s) to return on a transaction search?
public static List<AssemblyBuildResult> Get()
{
var listAssemblyBuilds = new List<AssemblyBuildResult>();
var service = Service.Context();
var ts = new TransactionSearch();
var tsb = new TransactionSearchBasic();
var sfType = new SearchEnumMultiSelectField
{
#operator = SearchEnumMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new string[] { "_assemblyBuild" }
};
tsb.type = sfType;
ts.basic = tsb;
ts.inventoryDetailJoin = new InventoryDetailSearchBasic();
// perform the search
var response = service.search(ts);
response.pageSizeSpecified = true;
// Process response
if (response.status.isSuccess)
{
// Process the records returned in the response
// Get more records with pagination
if (response.totalRecords > 0)
{
for (var x = 1; x <= response.totalPages; x++)
{
var records = response.recordList;
foreach (var t in records)
{
var ab = (AssemblyBuild) t;
listAssemblyBuilds.Add(GetAssemblyBuildsResult(ab));
}
if (response.pageIndex < response.totalPages)
{
response = service.searchMoreWithId(response.searchId, x + 1);
}
}
}
}
// Parse and return NetSuite WorkOrder into assembly WorkOrderResult list
return listAssemblyBuilds;
}
After much pain and suffering, I was able to solve this problem with the following code:
/// <summary>
/// Returns List of AssemblyBuilds from NetSuite
/// </summary>
/// <returns></returns>
public static List<AssemblyBuildResult> Get(string id = "", bool getDetails = false)
{
// Object to populate and return results
var listAssemblyBuilds = new List<AssemblyBuildResult>();
// Initiate Service and SavedSearch (TransactionSearchAdvanced)
var service = Service.Context();
var tsa = new TransactionSearchAdvanced
{
savedSearchScriptId = "customsearch_web_assemblysearchmainlist"
};
// Filter by ID if specified
if (id != "")
{
tsa.criteria = new TransactionSearch()
{
basic = new TransactionSearchBasic()
{
internalId = new SearchMultiSelectField
{
#operator = SearchMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new[] {
new RecordRef() {
type = RecordType.assemblyBuild,
typeSpecified = true,
internalId = id
}
}
}
}
};
}
// Construct custom columns to return
var tsr = new TransactionSearchRow();
var tsrb = new TransactionSearchRowBasic();
var orderIdCols = new SearchColumnSelectField[1];
var orderIdCol = new SearchColumnSelectField();
orderIdCols[0] = orderIdCol;
tsrb.internalId = orderIdCols;
var tranDateCols = new SearchColumnDateField[1];
var tranDateCol = new SearchColumnDateField();
tranDateCols[0] = tranDateCol;
tsrb.tranDate = tranDateCols;
var serialNumberCols = new SearchColumnStringField[1];
var serialNumberCol = new SearchColumnStringField();
serialNumberCols[0] = serialNumberCol;
tsrb.serialNumbers = serialNumberCols;
// Perform the Search
tsr.basic = tsrb;
tsa.columns = tsr;
var response = service.search(tsa);
// Process response
if (response.status.isSuccess)
{
var searchRows = response.searchRowList;
if (searchRows != null && searchRows.Length >= 1)
{
foreach (SearchRow t in searchRows)
{
var transactionRow = (TransactionSearchRow)t;
listAssemblyBuilds.Add(GetAssemblyBuildsResult(transactionRow, getDetails));
}
}
}
// Parse and return NetSuite WorkOrder into assembly WorkOrderResult list
return listAssemblyBuilds;
}
private static string GetAssemblyBuildLotNumbers(string id)
{
var service = Service.Context();
var serialNumbers = "";
var tsa = new TransactionSearchAdvanced
{
savedSearchScriptId = "customsearch_web_assemblysearchlineitems"
};
service.searchPreferences = new SearchPreferences { bodyFieldsOnly = false };
tsa.criteria = new TransactionSearch()
{
basic = new TransactionSearchBasic()
{
internalId = new SearchMultiSelectField
{
#operator = SearchMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new[] {
new RecordRef() {
type = RecordType.assemblyBuild,
typeSpecified = true,
internalId = id
}
}
}
}
};
// Construct custom columns to return
var tsr = new TransactionSearchRow();
var tsrb = new TransactionSearchRowBasic();
var orderIdCols = new SearchColumnSelectField[1];
var orderIdCol = new SearchColumnSelectField();
orderIdCols[0] = orderIdCol;
tsrb.internalId = orderIdCols;
var serialNumberCols = new SearchColumnStringField[1];
var serialNumberCol = new SearchColumnStringField();
serialNumberCols[0] = serialNumberCol;
tsrb.serialNumbers = serialNumberCols;
tsr.basic = tsrb;
tsa.columns = tsr;
var response = service.search(tsa);
if (response.status.isSuccess)
{
var searchRows = response.searchRowList;
if (searchRows != null && searchRows.Length >= 1)
{
foreach (SearchRow t in searchRows)
{
var transactionRow = (TransactionSearchRow)t;
if (transactionRow.basic.serialNumbers != null)
{
return transactionRow.basic.serialNumbers[0].searchValue;
}
}
}
}
return serialNumbers;
}
private static AssemblyBuildResult GetAssemblyBuildsResult(TransactionSearchRow tsr, bool getDetails)
{
if (tsr != null)
{
var assemblyInfo = new AssemblyBuildResult
{
NetSuiteId = tsr.basic.internalId[0].searchValue.internalId,
ManufacturedDate = tsr.basic.tranDate[0].searchValue,
SerialNumbers = tsr.basic.serialNumbers[0].searchValue
};
// If selected, this will do additional NetSuite queries to get detailed data (slower)
if (getDetails)
{
// Look up Lot Number
assemblyInfo.LotNumber = GetAssemblyBuildLotNumbers(tsr.basic.internalId[0].searchValue.internalId);
}
return assemblyInfo;
}
return null;
}
What I learned about pulling data from NetSuite:
Using SavedSearches is the best method to pull data that doesn't automatically come through in the API objects
It is barely supported
Don't specify an ID on the SavedSearch, specify a criteria in the TransactionSearch to get one record
You will need to specify which columns to actually pull down. NetSuite doesn't just send you the data from a SavedSearch automatically
You cannot view data in a SavedSearch that contains a Grouping
In the Saved Search, use the Criteria Main Line = true/false to read data from the main record (top of UI screen), and line items (bottom of screen)
I am using Rotativa to generate PDF in my "MVC" application. How can I save Rotativa PDF? I need to save the document on a server after all the process is completed.
Code below:
public ActionResult PRVRequestPdf(string refnum,string emid)
{
var prv = functions.getprvrequest(refnum, emid);
return View(prv);
}
public ActionResult PDFPRVRequest()
{
var prv = Session["PRV"] as PRVRequestModel;
byte[] pdfByteArray = Rotativa.WkhtmltopdfDriver.ConvertHtml("Rotativa", "Approver", "PRVRequestPdf");
return new Rotativa.ViewAsPdf("PRVRequestPdf", new { refnum = prv.rheader.request.Referenceno });
}
You can give this a try
var actionResult = new ActionAsPdf("PRVRequestPdf", new { refnum = prv.rheader.request.Referenceno, emid = "Whatever this is" });
var byteArray = actionResult.BuildPdf(ControllerContext);
var fileStream = new FileStream(fullPath, FileMode.Create, FileAccess.Write);
fileStream.Write(byteArray, 0, byteArray.Length);
fileStream.Close();
If that doesn't do the trick then, you can follow the answers here
Just make sure if you do it this way not to have PRVRequestPdf return as a PDF View, rather a normal View like you have above (only mention as managed to fall foul of that myself causing lots of fun).
Another useful answer:
I found the solution here
var actionPDF = new Rotativa.ActionAsPdf("YOUR_ACTION_Method", new { id = ID, lang = strLang } //some route values)
{
//FileName = "TestView.pdf",
PageSize = Size.A4,
PageOrientation = Rotativa.Options.Orientation.Landscape,
PageMargins = { Left = 1, Right = 1 }
};
byte[] applicationPDFData = actionPDF.BuildPdf(ControllerContext);
This is the original thread
You can achieve this with ViewAsPdf.
[HttpGet]
public ActionResult SaveAsPdf(string refnum, string emid)
{
try
{
var prv = functions.getprvrequest(refnum, emid);
ViewAsPdf pdf = new Rotativa.ViewAsPdf("PRVRequestPdf", prv)
{
FileName = "Test.pdf",
CustomSwitches = "--page-offset 0 --footer-center [page] --footer-font-size 8"
};
byte[] pdfData = pdf.BuildFile(ControllerContext);
string fullPath = #"\\server\network\path\pdfs\" + pdf.FileName;
using (var fileStream = new FileStream(fullPath, FileMode.Create, FileAccess.Write))
{
fileStream.Write(pdfData, 0, pdfData.Length);
}
return Json(new { isSuccessful = true }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
//TODO: ADD LOGGING
return Json(new { isSuccessful = false, error = "Uh oh!" }, JsonRequestBehavior.AllowGet);
//throw;
}
}
You can simply try this:
var fileName = string.Format("my_file_{0}.pdf", id);
var path = Server.MapPath("~/App_Data/" + fileName);
System.IO.File.WriteAllBytes(path, pdfByteArray );
I have a method that calls stored procedure and returns the data after executing DataReader.
I am trying to test the method using mock. I am not sure how to return value?
Anyone did this? Appreciate your responses.
Here is my code:
// Call the StoredProcedure
public List<string> GetCompletedBatchList(int fileId)
{
List<string> completedBatches = new List<string>();
StoredProcedure sp = new StoredProcedure("GetDistributedBatches", this.dataProvider);
sp.Command.AddParameter("FileID", fileId, DbType.Int32, ParameterDirection.Input);
sp.Command.AddParameter("Result", null, DbType.Int32, ParameterDirection.InputOutput);
using (var rdr = sp.ExecuteReader())
{
while (rdr != null && rdr.Read())
{
if (rdr[0] != null)
{
completedBatches.Add(rdr[0].ToString());
}
}
}
return completedBatches;
}
Here is the Test Method:
[Test]
public void Can_get_completedBatches()
{
var file = new File() { FileID = 1, DepositDate = DateTime.Now };
repo.Add<File>(file);
CompletedBatches completedBatches = new CompletedBatches(provider.Object);
//Here I am not sure how to Return
provider.Setup(**x => x.ExecuteReader(It.IsAny<QueryCommand>())).Returns** =>
{
cmd.OutputValues.Add(0);
});
var completedBatchesList = completedBatches.GetCompletedBatchList(file.FileID);
Assert.AreEqual(0, completedBatchesList.Count());
}
If you want to create a DataReader of a certain shape and size then I suggest you look at DataTable.CreateDataReader DataTable.CreateDataReader. You can then setup the ExecuteReader in your example to return this datareader.
The following link helped me...
How to mock an SqlDataReader using Moq - Update
I used MockDbDataReader method to mock the data
[Test]
public void Can_get_completedBatches_return_single_batch()
{
var date = DateTime.Now;
var file = new File() { FileID = 202, DepositDate = DateTime.Now };
var batch1 = new Batch() { FileID = 202, BatchID = 1767, LockboxNumber = "1", IsLocked = true, LockedBy = "testUser" };
var transaction1 = new Transaction() { BatchID = 1767, TransactionID = 63423, CheckAmount = 100.0 };
var distribution1 = new Distribution() { TransactionID = 63423, InvoiceNumber = "001", Amount = 100.0, DateCreated = date, DateModified = date, TransType = 2 };
repo.Add<File>(file);
repo.Add<Batch>(batch1);
repo.Add<Transaction>(transaction1);
repo.Add<Distribution>(distribution1);
CompletedBatches completedBatches = new CompletedBatches(provider.Object);
provider.Setup(x => x.ExecuteReader(It.IsAny<QueryCommand>())).Returns(MockDbDataReader());
var completedBatchesList = completedBatches.GetCompletedBatchList(202);
Assert.AreEqual(1, completedBatchesList.Count());
}
// You should pass here a list of test items, their data
// will be returned by IDataReader
private DbDataReader MockDbDataReader(List<TestData> ojectsToEmulate)
{
var moq = new Mock<DbDataReader>();
// This var stores current position in 'ojectsToEmulate' list
int count = -1;
moq.Setup(x => x.Read())
// Return 'True' while list still has an item
.Returns(() => count < ojectsToEmulate.Count - 1)
// Go to next position
.Callback(() => count++);
moq.Setup(x => x["BatchID"])
// Again, use lazy initialization via lambda expression
.Returns(() => ojectsToEmulate[count].ValidChar);
return moq.Object;
}