I´m developing my first app in Xamarin
, and got one problem, I´m trying to make bulk insert
to insert more faster and decrease time synchronization
with my rest api
.
So I tried so many ways to avoid it, but they didn´t work.
Using singleton connection, adding parameters when create connection and use using
keyword to dispose
the connection.
I have 9000 registers, and got and then paginated around 1000 registers for each time.
When it comes at the last pagination page, it stop and console gave a message like this.
11-19 11:52:17.453 I/art (32524): Starting a blocking GC Explicit
11-19 11:52:17.463 I/art (32524): Explicit concurrent mark sweep GC freed 97(4KB) AllocSpace objects, 0(0B) LOS objects, 27% free, 10MB/14MB, paused 380us total 15.150ms
11-19 11:52:17.463 D/Mono (32524): GC_TAR_BRIDGE bridges 120 objects 126 opaque 9 colors 120 colors-bridged 120 colors-visible 120 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.09ms tarjan 0.11ms scc-setup 0.16ms gather-xref 0.01ms xref-setup 0.00ms cleanup 0.04ms
11-19 11:52:17.463 D/Mono (32524): GC_BRIDGE: Complete, was running for 18.02ms
11-19 11:52:17.463 D/Mono (32524): GC_MAJOR: (LOS overflow) time 50.76ms, stw 60.27ms los size: 4096K in use: 1369K
11-19 11:52:17.463 D/Mono (32524): GC_MAJOR_SWEEP: major size: 8208K in use: 6622K
11-19 11:52:17.693 D/Mono (32524): GC_TAR_BRIDGE bridges 0 objects 0 opaque 0 colors 0 colors-bridged 0 colors-visible 120 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.09ms tarjan 0.11ms scc-setup 0.16ms gather-xref 0.01ms xref-setup 0.00ms cleanup 0.00ms
11-19 11:52:17.693 D/Mono (32524): GC_BRIDGE: Complete, was running for 0.10ms
11-19 11:52:17.693 D/Mono (32524): GC_MINOR: (Nursery full) time 2.45ms, stw 3.05ms promoted 10K major size: 8240K in use: 6634K los size: 4096K in use: 1650K
11-19 11:52:17.933 D/Mono (32524): GC_TAR_BRIDGE bridges 0 objects 0 opaque 0 colors 0 colors-bridged 0 colors-visible 120 xref 0 cache-hit 0 cache-semihit 0 cache-miss 0 setup 0.09ms tarjan 0.11ms scc-setup 0.16ms gather-xref 0.01ms xref-setup 0.00ms cleanup 0.00ms
11-19 11:52:17.933 D/Mono (32524): GC_BRIDGE: Complete, was running for 0.28ms
11-19 11:52:17.933 D/Mono (32524): GC_MINOR: (Nursery full) time 2.42ms, stw 3.03ms promoted 2K major size: 8240K in use: 6636K los size: 4096K in use: 1916K
Bulk insert method
public void BulkInsert(JArray array, string tableName = "")
{
try
{
if (string.IsNullOrEmpty(tableName))
{
Type typeParameterType = typeof(T);
tableName = typeParameterType.Name;
}
StringBuilder stringBuilder = new StringBuilder();
array.ToList().ForEach(register =>
{
stringBuilder.AppendLine(DataBaseUtil.GenerateInsertStatement(register, tableName));
});
SQLiteCommand command = new SQLiteCommand(ConnectionDataBase.Connection);
command.CommandText = stringBuilder.ToString();
command.ExecuteNonQuery();
}
catch (Exception e)
{
LogUtil.WriteLog(e);
}
public static string GenerateInsertStatement(JToken register, string tableName)
{
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(register.ToString());
string columns = string.Join(",", data.Keys.ToList());
string values = string.Join(",", data.Values.Select(v => string.Format(@"'{0}'", v.Trim())));
return string.Format("INSERT INTO {0} ({1}) VALUES ({2}); ", tableName, columns, values);
}
The method which works with so many records
private async Task GenerateOrdensServico()
{
try
{
_logs.Add("ORDENS DE SERVIÇO");
double increment = ((1 - Progresso) / 2);
int records = await _ordemServicoRest.GetCount();
int limit = _sistemaParametroRepository.GetTamanhoPaginaSincMobile();
int pages = (records / limit);
for (int i = 0; i <= pages; i++)
{
JArray ordensServico = await _ordemServicoRest.GetAllInJsonFormatPaginated(DataBaseUtil.GetPagination(i, limit));
if (ordensServico == null)
{
_logs.Add("Não Contem O.S de Corte para importar!");
await App.Current.MainPage.DisplayAlert("Atenção", "Não tem O.S para importar!", "OK");
continue;
}
_ordemServicoRepository.BulkInsert(ordensServico);
AlterProgress(Progresso += ((Progresso * 100) + (increment / pages)));
}
}
catch (Exception e)
{
LogUtil.WriteLog(e);
}
}
My http get method
protected async Task<Object> GetValue(string metodo = "", string parametros = "")
{
try
{
_urlBase = new Uri(GetStringConnectionParameters(metodo) + parametros);
using (HttpClient client = new HttpClient())
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, _urlBase))
using (HttpResponseMessage response = await client.SendAsync(request))
{
Stream content = await response.Content.ReadAsStreamAsync();
if (response.IsSuccessStatusCode)
{
return DeserializeJsonFromStream<Object>(content);
}
}
}
catch (Exception e)
{
LogUtil.WriteLog(e);
}
return null;
}
And the method who calls the list of entities that I need to insert in the database
private async Task RegistrarDados()
{
try
{
_logs.Add("Realizando o Download: ");
AlterProgress(12.5);
await GenerateAtendimentoMotivosEncerramento();
AlterProgress(15);
await GenerateHidrometrosLocalInstalacao();
AlterProgress(17.5);
await GenerateHidrometrosProtecao();
AlterProgress(20);
await GenerateFuncionarios();
AlterProgress(25);
await GenerateGrupoFaturamento();
AlterProgress(30);
await GenerateLigacaoAguaSituacoes();
AlterProgress(40);
await GenerateLigacaoEsgotoSituacoes();
AlterProgress(45);
await GenerateServicosTipo();
AlterProgress(50);
await GenerateSistemParametros();
AlterProgress(55);
await GenerateOrdensServico();
AlterProgress(55);
await GenerateContas();
int contador = _ordemServicoRepository.Count<OrdemServico>();
AlterProgress(100);
_logs.Add("Sincronização encerrada com sucesso!");
await App.Current.MainPage.DisplayAlert("Atenção", "Foram importados " + contador + " Ordens de Serviços!", "OK");
PodeSincronizar = true;
}
catch (Exception e)
{
LogUtil.WriteLog(e);
}
}
The methods GenerateSistemParametros(); GenerateOrdensServico(); GenerateContas();
needs to execute in this order because they have dependance.
Edit
I also tried first this method to save
public void BulkInsert(JArray array, string tableName = "")
{
try
{
if (string.IsNullOrEmpty(tableName))
{
Type typeParameterType = typeof(T);
tableName = typeParameterType.Name;
}
using (SQLiteConnection connection = new SQLiteConnection(DataBaseUtil.GetDataBasePath()))
{
connection.BeginTransaction();
array.ToList().ForEach(register =>
{
string sql = DataBaseUtil.GenerateInsertStatement(register, tableName);
System.Diagnostics.Debug.WriteLine(sql);
var command = connection.CreateCommand(sql);
command.ExecuteNonQuery();
});
connection.Commit();
DataBaseUtil.CloseConnection(connection);
}
}
catch (Exception e)
{
LogUtil.WriteLog(e);
}
}