I am using ExecuteMultipleResponse method to insert 10 account records at a time using SSIS.
List<Entity> _Accounts = new List<Entity>();
// Check the batch size and process
public override void InputAccount_ProcessInput(InputAccountBuffer Buffer)
{
//List<int> personIDs = new List<int>();
int index = 0;
while (Buffer.NextRow())
{
_Accounts.Add(InputAccountFromBuffer(Buffer));
//personIDs.Add(int.Parse(Buffer.sPersonID));
index++;
if (index == 10)
{
ImportBatch();
index = 0;
}
}
ImportBatch();
}
private void ImportBatch()
{
if (_Accounts.Count > 0)
{
var multipleRequest = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = true,
ReturnResponses = true
},
Requests = new OrganizationRequestCollection()
};
foreach (var profContact in _Accounts)
{
CreateRequest reqCreate = new CreateRequest();
reqCreate.Target = profContact;
reqCreate.Parameters.Add("SuppressDuplicateDetection", false);
multipleRequest.Requests.Add(reqCreate);
}
ExecuteMultipleResponse multipleResponses = (ExecuteMultipleResponse)organizationservice.Execute(multipleRequest);
var responses = (ExecuteMultipleResponseItemCollection)multipleResponses.Results["Responses"];
foreach (var response in responses)
{
if (response.Fault != null)
{
// A fault has occurred, handle it here
}
else
{
// THIS IS WHERE I KNOW THE GUID VALUE EXIST.
}
}
//IEnumerator f = multipleResponses.Responses.GetEnumerator();
_Accounts.Clear();
}
}
Above code is working fine, however, I now need to read and store Guids from response to a List. This information is essential for the next step in the package. I know, if I am creating single record I can simply say,
Guid newRecord = _service.Create(account);
I even managed to get down to check if the response have 'Fault' or not and if it doesn't have fault then Guid value should exist in the response.
Running response.Response.Results.Values in QuickWatch shows me the guid but I just can't find a way to read it directly and store it as a Guid.
The guid of a created record should be stored in the OrganizationResponse which can be found inside the ExecuteMultipleResponseItem
Try the following to get the guid as a string:
string id = response.Response.Results["id"].ToString()
If it works as expected you should also be able to instantiate a guid, if needed:
Guid guid = new Guid(id);
Related
I wish to automate the transfer of ether to a list of people.
Assume the list is in a csv.
I wrote some code to automate the process.
class Program
{
int nonce = 0;
static void Main(string[] args)
{
var account = SetupAccount();
var recipients = ReadCsv();
var web3Init = GetConnection();
nonce = web3.Eth.Transactions.GetTransactionCount.SendRequestAsync(account.Address).Result;
//var recipients = new List<Records>() { new Records() { Value = 10000000000000000, Address = "0x5CC494843e3f4AC175A5e730c300b011FAbF2cEa" } };
foreach (var recipient in recipients)
{
try
{
var web3 = GetConnection();
var receipt = SendEther(account, recipient, web3).Result;
}
catch (System.Exception)
{
MessageBox.Show("Failed");
}
Thread.Sleep(30000);
}
}
private static async Task<TransactionReceipt> SendEther(Account account, Records recipient, Web3 web3)
{
var transactionPolling = web3.TransactionManager.TransactionReceiptService;
//var currentBalance = await web3.Eth.GetBalance.SendRequestAsync(account.Address);
//assumed client is mining already
//when sending a transaction using an Account, a raw transaction is signed and send using the private key
return await transactionPolling.SendRequestAndWaitForReceiptAsync(() =>
{
var transactionInput = new TransactionInput
{
From = account.Address,
//Gas = new HexBigInteger(25000),
GasPrice = new HexBigInteger(10 ^ 10),
To = recipient.Address,
Value = new HexBigInteger(new BigInteger(recipient.Value)),
Nonce = nonce
};
var txSigned = new Nethereum.Signer.TransactionSigner();
var signedTx = txSigned.SignTransaction(account.PrivateKey, transactionInput.To, transactionInput.Value, transactionInput.Nonce);
var transaction = new Nethereum.RPC.Eth.Transactions.EthSendRawTransaction(web3.Client);
nonce++;
return transaction.SendRequestAsync(signedTx);
});
}
private static Web3 GetConnection()
{
return new Web3("https://mainnet.infura.io");
}
private static Account SetupAccount()
{
var password = "#Password";
var accountFilePath = #"filePath";
return Account.LoadFromKeyStoreFile(accountFilePath, password);
}
private static List<Records> ReadCsv()
{
string filePath = #"C:\Users\Potti\source\repos\ConversionFiles\XrcfRecipients.csv";
if (File.Exists(filePath))
{
using (StreamReader stream = new StreamReader(filePath))
{
CsvReader reader = new CsvReader(stream, new Configuration
{
TrimOptions = TrimOptions.Trim,
HasHeaderRecord = true,
HeaderValidated = null
});
reader.Configuration.RegisterClassMap<RecordMapper>();
return reader.GetRecords<Records>().ToList();
}
}
else
{
return null;
}
}
}
class Records
{
public string Address { get; set; }
public decimal Value { get; set; }
}
sealed class RecordMapper : ClassMap<Records>
{
public RecordMapper()
{
Map(x => x.Address).Name("Address");
Map(x => x.Value).Name("Value");
}
}
How do i modify the process to execute all the transactions at once instead of waiting for each to complete? (Fire and forget)
Also, are there any security considerations of doing this?
What you are currently doing is waiting for each transaction to be mined. What you can do is the following:
var account = new Account("privateKey"); // or load it from your keystore file as you are doing.
var web3 = new Web3(account, "https://mainnet.infura.io");
First create a web3 instance using the same Account object, because we are using an account with a private key, Nethereum will sign your transactions offline before sending them.
Now using the TransactionManager, you can then send a transaction per each recepient
var transactionHashes = new List<string>();
foreach(var recepient in recepients){
var transactionInput = new TransactionInput
{
From = account.Address,
GasPrice = Web3.Convert.ToWei(1.5, UnitConversion.EthUnit.Gwei);,
To = recipient.Address,
Value = new HexBigInteger(new BigInteger(recipient.Value)),
};
var transactionHash = web3.Eth.TransactionManager.SendTransactionAsync(transactionInput);
transanctionHashes.Add(transactionHash);
}
Note that when Nethereum uses the same instance of an Account and TransactionManager (or Web3 in this scenario) it creates a default NonceMemoryService, so you don't need to keep track of your nonces (Transaction number), to sign the transaction.
Also I have done a conversion for the GasPrice from Gwei to Wei, as an example of Unit conversions, I assume that you already have converted to Wei the Ether amounts you are going to send.
Finally, another note, to further simplify this, there is an upcoming EtherTransferService which allows you to input Ether amounts and Gwei price amounts to avoid doing conversions. Also the gas price will be calculated for you, if not passed any parameter.
web3.Eth.GetEtherTransferService().TransferEtherAsync("toAddress", EtherAmount);
this might be a simple answer due to my inexperience with C# and .NET. I have two Stripe Test Accounts. TL:DR; is I am essentially looking for a Customers.all solution.
The source account has all the customer, card, and charge data.
The destination account has the copied card and customer data done by Stripe Support.
I have code that loops through the pulls all the data from the source account. It then finds the customer data from the destination account using the collection of customer/card data from the source. After that it then recreates the charges from the source account into the destination account.
I am able to successfully copy the first 100 charges into the destination account using information from the source account but I am having the hardest time getting the rest of the customers.
This is what I have so far:
public static void GenerateDestinationChargeData()
{
// code to get collection of customer data from destination account
StripeConfiguration.SetApiKey(destinationTestKey);
var customerService = new StripeCustomerService();
IEnumerable<StripeCustomer> customerItems = customerService.List(
new StripeCustomerListOptions()
{
Limit = 100,
//this is what I cannot figure out, eventually to get all of the customers from the destination account
StartingAfter = customerItems.LastOrDefault().Id
}
);
// loop through collection of customers from destination acct to fetch customer charge data from source account
foreach (var i in customerItems)
{
bool isError = false;
var liveChargeService = new StripeChargeService();
StripeConfiguration.SetApiKey(sourceTestKey);
StripeList<StripeCharge> chargeItems = new StripeList<StripeCharge>();
chargeItems = liveChargeService.List(
new StripeChargeListOptions()
{
Limit = 100,
CustomerId = i.Id
}
);
// loop through customer charge data from source and re-create charge data on destination Acct
foreach (var c in chargeItems.Data)
{
StripeConfiguration.SetApiKey(sourceTestKey);
var emailReceipt = "";
Dictionary<string, string> chargeMetaData = new Dictionary<string, string>();
var onBehalfOf = "";
var transferGroup = "";
var chargeDescription = "";
var chargeCaptured = "";
var chargeCurrency = "";
var chargeStatementDescriptor = "";
if (c.ReceiptEmail != null)
{
emailReceipt = c.ReceiptEmail;
}
if (c.Metadata != null)
{
chargeMetaData = c.Metadata;
}
if (c.OnBehalfOf != null)
{
onBehalfOf = c.OnBehalfOf.ToString();
}
if (c.TransferGroup != null)
{
transferGroup = c.TransferGroup;
}
if (c.Description != null)
{
chargeDescription = c.Description;
}
if (c.Captured != null)
{
chargeCaptured = c.Captured.ToString();
}
if (c.Currency != null)
{
chargeCurrency = c.Currency;
}
if (c.StatementDescriptor != null)
{
chargeStatementDescriptor = c.StatementDescriptor;
}
try
{
var chargeOptions = new StripeChargeCreateOptions();
chargeOptions.CustomerId = i.Id;
chargeOptions.ReceiptEmail = emailReceipt;
chargeOptions.Metadata = chargeMetaData;
chargeOptions.Description = chargeDescription;
chargeOptions.Capture = c.Captured;
chargeOptions.Currency = chargeCurrency;
chargeOptions.Amount = c.Amount;
chargeOptions.StatementDescriptor = chargeStatementDescriptor;
StripeChargeService chargeService = new StripeChargeService(destinationTestKey);
StripeCharge stripeCharge = chargeService.Create(chargeOptions);
}
catch (Exception ex)
{
Utility.NotifyDevAdminException("test", ex);
isError = true;
}
if (isError) continue;
}
}
}
Thank you so much :)
Since we cannot do a Customers.all with this current Stripe API, the solution is to set an empty variable and assign it the last Customer ID in the first set of 100 that we get and continue the query from that last assigned value
var lastId = String.Empty;
if (String.IsNullOrEmpty(lastId))
{
StripeConfiguration.SetApiKey(sourceCustomerAccountAPIKey);
customerItems = customerService.List(
new StripeCustomerListOptions(){ Limit = 100 });
}
else
{
StripeConfiguration.SetApiKey(sourceCustomerAccountAPIKey);
customerItems = customerService.List(
new StripeCustomerListOptions() {
Limit = 100,
StartingAfter = lastId });
}
lastId = customerItems.LastOrDefault().Id;
I am using ExecuteMultipleResponse method to insert 10 account records at a time using SSIS.
List<Entity> _Accounts = new List<Entity>();
// Check the batch size and process
public override void InputAccount_ProcessInput(InputAccountBuffer Buffer)
{
//List<int> personIDs = new List<int>();
int index = 0;
while (Buffer.NextRow())
{
_Accounts.Add(InputAccountFromBuffer(Buffer));
//personIDs.Add(int.Parse(Buffer.sPersonID));
index++;
if (index == 10)
{
ImportBatch();
index = 0;
}
}
ImportBatch();
}
private void ImportBatch()
{
if (_Accounts.Count > 0)
{
var multipleRequest = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = true,
ReturnResponses = true
},
Requests = new OrganizationRequestCollection()
};
foreach (var profContact in _Accounts)
{
CreateRequest reqCreate = new CreateRequest();
reqCreate.Target = profContact;
reqCreate.Parameters.Add("SuppressDuplicateDetection", false);
multipleRequest.Requests.Add(reqCreate);
}
ExecuteMultipleResponse multipleResponses = (ExecuteMultipleResponse)organizationservice.Execute(multipleRequest);
var responses = (ExecuteMultipleResponseItemCollection)multipleResponses.Results["Responses"];
foreach (var response in responses)
{
if (response.Fault != null)
{
// A fault has occurred, handle it here
}
else
{
// THIS IS WHERE I KNOW THE GUID VALUE EXIST.
}
}
//IEnumerator f = multipleResponses.Responses.GetEnumerator();
_Accounts.Clear();
}
}
Above code is working fine, however, I now need to read and store Guids from response to a List. This information is essential for the next step in the package. I know, if I am creating single record I can simply say,
Guid newRecord = _service.Create(account);
I even managed to get down to check if the response have 'Fault' or not and if it doesn't have fault then Guid value should exist in the response.
Running response.Response.Results.Values in QuickWatch shows me the guid but I just can't find a way to read it directly and store it as a Guid.
The guid of a created record should be stored in the OrganizationResponse which can be found inside the ExecuteMultipleResponseItem
Try the following to get the guid as a string:
string id = response.Response.Results["id"].ToString()
If it works as expected you should also be able to instantiate a guid, if needed:
Guid guid = new Guid(id);
I have a scenario in CRM where I need to update existing accounts with their Vat and Registration number. There is well over 30 thousand accounts in the system. I am trying to update using the CRM SDK API but I am battling to figure out how to perform the actual update. The vat number and reg have been provided to me in a spreadsheet with their corresponding number, please note that the accounts are already in CRM so I just need to update the correct account with its Vat and Reg number, How can I do this in CRM, please advice on my code below:
public static void UpdateAllCRMAccountsWithVATAndRegistrationNumber(IOrganizationService service)
{
QueryExpression qe = new QueryExpression();
qe.EntityName = "account";
qe.ColumnSet = new ColumnSet("account", "new_vatno", "new_registrationnumber");
qe.Criteria.AddCondition("accountnumber", ConditionOperator.In,"TA10024846", "TA10028471", "TA20014015", "TA4011652", "TA4011557");
EntityCollection response = service.RetrieveMultiple(qe);
foreach (var acc in response.Entities)
{
acc.Attributes["new_vatno"] = //this is where I am struggling to figure out how I am gong to match the records up,
acc.Attributes["new_registrationnumber"] = //this is where I am struggling to figure out how I am gong to match the records up,
service.Update(acc);
}
}
How am I going to ensure that I update the correct records. I have the vat and reg numbers for the accounts in a spreadsheet, please see example image below. Can I please get advised here. Thanks.
I would load the list of VAT updates from the spreadsheet into a dictionary and then load the 30k record from CRM into memory. Then I would match them up and use ExecuteMultipleRequest to do the updates. Alternatively, you could query CRM using the account numbers (if the list is small enough.) I made the assumption you had thousands of updates to do across the record set of 30k. Note, if the Account record size was very large and couldn't be loaded into memory you would need to do account number queries.
Here is the rough code for the basic solution (I haven't tested, method should be split up, and there is minimal error handling):
public class VatInfo
{
public string RegistrationNumber;
public string TaxNumber;
public static Dictionary<string, VatInfo> GetVatList()
{
//TODO: Implement logic to load CSV file into a list. Dictionary key value should be Account Number
throw new NotImplementedException();
}
}
public class UpdateVatDemo
{
public const int maxBatchSize = 100;
public static void RunVatUpdate(IOrganizationService conn)
{
var vats = VatInfo.GetVatList();
var pagingQuery = new QueryExpression("account");
pagingQuery.ColumnSet = new ColumnSet("accountnumber");
Queue<Entity> allEnts = new Queue<Entity>();
while (true)
{
var results = conn.RetrieveMultiple(pagingQuery);
if (results.Entities != null && results.Entities.Any())
results.Entities.ToList().ForEach(allEnts.Enqueue);
if (!results.MoreRecords) break;
pagingQuery.PageInfo.PageNumber++;
pagingQuery.PageInfo.PagingCookie = results.PagingCookie;
}
ExecuteMultipleRequest emr = null;
while (allEnts.Any())
{
if (emr == null)
emr = new ExecuteMultipleRequest()
{
Settings = new ExecuteMultipleSettings()
{
ContinueOnError = true,
ReturnResponses = true
},
Requests = new OrganizationRequestCollection()
};
var ent = allEnts.Dequeue();
if (vats.ContainsKey(ent.GetAttributeValue<string>("accountnumber")))
{
var newEnt = new Entity("account", ent.Id);
newEnt.Attributes.Add("new_vatno", vats[ent.GetAttributeValue<string>("accountnumber")].TaxNumber);
newEnt.Attributes.Add("new_registrationnumber", vats[ent.GetAttributeValue<string>("accountnumber")].RegistrationNumber);
emr.Requests.Add(new UpdateRequest() { Target = newEnt });
}
if (emr.Requests.Count >= maxBatchSize)
{
try
{
var emResponse = (ExecuteMultipleResponse) conn.Execute(emr);
foreach (
var responseItem in emResponse.Responses.Where(responseItem => responseItem.Fault != null))
DisplayFault(emr.Requests[responseItem.RequestIndex],
responseItem.RequestIndex, responseItem.Fault);
}
catch (Exception ex)
{
Console.WriteLine($"Exception during ExecuteMultiple: {ex.Message}");
throw;
}
emr = null;
}
}
}
private static void DisplayFault(OrganizationRequest organizationRequest, int count,
OrganizationServiceFault organizationServiceFault)
{
Console.WriteLine(
"A fault occurred when processing {1} request, at index {0} in the request collection with a fault message: {2}",
count + 1,
organizationRequest.RequestName,
organizationServiceFault.Message);
}
}
Updating the fetched entity is bound to fail because of its entity state, which would not be null.
To update the fetched entities, you need to new up the entity:
foreach (var acc in response.Entities)
{
var updateAccount = new Entity("account") { Id = acc.Id };
updateAccount .Attributes["new_vatno"] = null; //using null as an example.
updateAccount .Attributes["new_registrationnumber"] = null;
service.Update(acc);
}
Code below shows how I managed to get it righy. forst let me explain. I imported my records into a seperate SQL table, in my code I read that table into a list in memory, I then query CRM accounts that need to be updated, I then loop though each account and check if the account number in CRM matches the account number from my sql database, if it matches, I then update the relevant Reg no and Vat no, See code below:
List<Sheet1_> crmAccountList = new List<Sheet1_>();
//var crmAccount = db.Sheet1_.Select(x => x).ToList().Take(2);
var crmAccounts = db.Sheet1_.Select(x => x).ToList();
foreach (var dbAccount in crmAccounts)
{
CRMDataObject modelObject = new CRMDataObject()
{
ID = dbAccount.ID,
Account_No = dbAccount.Account_No,
Tax_No = dbAccount.Tax_No.ToString(),
Reg_No = dbAccount.Reg_No
//Tarsus_Country = dbAccount.Main_Phone
};
}
var officialDatabaseList = crmAccounts;
foreach (var crmAcc in officialDatabaseList)
{
QueryExpression qe = new QueryExpression();
qe.EntityName = "account";
qe.ColumnSet = new ColumnSet("accountnumber", "new_vatno", "new_registrationnumber");
qe.Criteria.AddCondition("accountnumber", ConditionOperator.In,'list of account numbers go here'
EntityCollection response = service.RetrieveMultiple(qe);
foreach (var acc in response.Entities)
{
if (crmAcc.Account_No == acc.Attributes["accountnumber"].ToString())
{
//acc.Attributes["new_vatno"] = crmAcc.VAT_No.ToString();
acc.Attributes["new_registrationnumber"] = crmAcc.Reg_No.ToString();
service.Update(acc);
}
}
}
I am currently trying to create a list of custom objects using data I get back from isolatedstorage, and deserializing it.
it worked perfectly yesterday and just keeps givin me this exception today, and I am not sure what to do?
{System.ArgumentOutOfRangeException: Length cannot be less than zero.
Parameter name: length
at System.String.InternalSubStringWithChecks(Int32 startIndex, Int32 length, Boolean fAlwaysCopy)
at System.String.Substring(Int32 startIndex, Int32 length)
at LandbouWP.ViewModel.StoryVM.GetStories(List`1 news_items)}
the code for getting the data and deserializing it:
var loaded_result = settings["mainlist"].ToString();
var s = JsonConvert.DeserializeObject<List<Object>>(loaded_result);
the deserializing works perfectly, so I don't think the issue is here, however does it maybe add another property or something to the list?
then I create a custom list of the returned items
App.StoryViewModel.GetStories(s);
and that code is:
public void GetStories(List<Object> news_items)
{
List<Story> a = new List<Story>();
List<Story> b = new List<Story>();
//loop over all items and add them for a viewmodel
int i = 0;
foreach (var item in news_items)
{
if (item.IsDeleted == true)
{
//do not add the item
}
else
{
try
{
a.Add(new Story
{
ID = news_items[i].ID,
IsDeleted = news_items[i].IsDeleted,
IsActive = news_items[i].IsActive,
Title = news_items[i].Title,
Author = news_items[i].Author,
Synopsis = news_items[i].Synopsis,
Body = news_items[i].Body,
ImageUrl = news_items[i].ImageUrl,
//CreationDate = DateTime.Parse(news_items[i].CreationDate),
CreationDate = news_items[i].CreationDate.Substring(0, news_items[i].CreationDate.IndexOf('T')),
LastUpdateDate = news_items[i].LastUpdateDate.Substring(0, news_items[i].LastUpdateDate.IndexOf('T')),
DisplayUntilDate = news_items[i].DisplayUntilDate.Substring(0, news_items[i].DisplayUntilDate.IndexOf('T')),
TotalViews = news_items[i].TotalViews,
Gallery = news_items[i].Gallery
});
i++;
}
catch (Exception ex)
{
string msg = ex.ToString();
string msg2 = msg;
}
}
}
//try here to remove duplicates?
foreach (var item in a)
{
if (!b.Contains(item))
{
b.Add(item);
}
else
{
b.Remove(item);
}
}
var new_list = b.OrderByDescending(x => x.CreationDate).ToList();
//save all the stories
story = new_list;
I cannot even go through each item individually that I am trying to set, it just throws length cannot be less than zero, and I am not sure what its talking about, I do not have a parameter in my class named Length?
Check carefully this place
CreationDate = news_items[i].CreationDate.Substring(0, news_items[i].CreationDate.IndexOf('T')),
LastUpdateDate = news_items[i].LastUpdateDate.Substring(0, news_items[i].LastUpdateDate.IndexOf('T')),
DisplayUntilDate = news_items[i].DisplayUntilDate.Substring(0, news_items[i].DisplayUntilDate.IndexOf('T')),
I suppose one of your dates just in wrong format and has no "T"