Improved tag processing code with fewer round trips to db
This commit is contained in:
@@ -73,56 +73,77 @@ namespace AyaNova.Biz
|
|||||||
public static async Task ProcessDeleteTagsInRepositoryAsync(AyContext ct, List<string> deleteTags)
|
public static async Task ProcessDeleteTagsInRepositoryAsync(AyContext ct, List<string> deleteTags)
|
||||||
{
|
{
|
||||||
if (deleteTags.Count == 0) return;
|
if (deleteTags.Count == 0) return;
|
||||||
|
|
||||||
|
var existing = await ct.Tag.Where(x => deleteTags.Contains(x.Name)).ToListAsync();
|
||||||
foreach (string s in deleteTags)
|
foreach (string s in deleteTags)
|
||||||
{
|
{
|
||||||
bool bDone = false;
|
var t = existing.FirstOrDefault(x => x.Name == s);
|
||||||
//Keep on trying until there is success
|
if (t != null)
|
||||||
//this allows for concurrency issues
|
|
||||||
//I considered a circuit breaker / timeout here, but theoretically it should not be an issue
|
|
||||||
//at some point it should not be a concurrency issue anymore
|
|
||||||
//And this is not critical functionality requiring a transaction and locking
|
|
||||||
|
|
||||||
//TODO: Move this into a stored procedure just like search keyword processing for efficiency
|
|
||||||
//it's likely not going to be an issue but still, it's not right to do it this way
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
//START: Get tag word and concurrency token and count
|
if (t.RefCount < 2)//catch any that fell through the cracks and are maybe zero or negative event
|
||||||
var ExistingTag = await ct.Tag.FirstOrDefaultAsync(z => z.Name == s);
|
ct.Remove(t);
|
||||||
//if not present, then nothing to do
|
else
|
||||||
if (ExistingTag != null)
|
t.RefCount -= 1;
|
||||||
{
|
}
|
||||||
//No longer needed?
|
|
||||||
if (ExistingTag.RefCount == 1)
|
|
||||||
{
|
|
||||||
ct.Remove(ExistingTag);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Decrement the refcount
|
|
||||||
ExistingTag.RefCount -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await ct.SaveChangesAsync();
|
|
||||||
bDone = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex) when (ex is DbUpdateConcurrencyException)//allow for possible other types
|
|
||||||
{
|
|
||||||
//allow others to flow past
|
|
||||||
// string sss = ex.ToString();
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
bDone=true;
|
|
||||||
}
|
|
||||||
} while (bDone == false);
|
|
||||||
}
|
}
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
#region OLD SLOW METHOD FOR REFERENCE IN CASE CONCURRENCY EXCEPTIONS
|
||||||
|
// foreach (string s in deleteTags)
|
||||||
|
// {
|
||||||
|
// bool bDone = false;
|
||||||
|
// //Keep on trying until there is success
|
||||||
|
// //this allows for concurrency issues
|
||||||
|
// //I considered a circuit breaker / timeout here, but theoretically it should not be an issue
|
||||||
|
// //at some point it should not be a concurrency issue anymore
|
||||||
|
// //And this is not critical functionality requiring a transaction and locking
|
||||||
|
// do
|
||||||
|
// {
|
||||||
|
// //START: Get tag word and concurrency token and count
|
||||||
|
// var ExistingTag = await ct.Tag.FirstOrDefaultAsync(z => z.Name == s);
|
||||||
|
// //if not present, then nothing to do
|
||||||
|
// if (ExistingTag != null)
|
||||||
|
// {
|
||||||
|
// //No longer needed?
|
||||||
|
// if (ExistingTag.RefCount == 1)
|
||||||
|
// {
|
||||||
|
// ct.Remove(ExistingTag);
|
||||||
|
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// //Decrement the refcount
|
||||||
|
// ExistingTag.RefCount -= 1;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// await ct.SaveChangesAsync();
|
||||||
|
// bDone = true;
|
||||||
|
// }
|
||||||
|
// catch (Exception ex) when (ex is DbUpdateConcurrencyException)//allow for possible other types
|
||||||
|
// {
|
||||||
|
// //allow others to flow past
|
||||||
|
// // string sss = ex.ToString();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// bDone = true;
|
||||||
|
// }
|
||||||
|
// } while (bDone == false);
|
||||||
|
// }
|
||||||
|
#endregion old slow method
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ProcessUpdateTagsInRepositoryAsync(AyContext ct, List<string> newTags, List<string> originalTags = null)
|
public static async Task ProcessUpdateTagsInRepositoryAsync(AyContext ct, List<string> newTags, List<string> originalTags = null)
|
||||||
{
|
{
|
||||||
//todo: Recode this as a procedure like search indexing or at least a direct sql call
|
//todo: Recode this as a procedure like search indexing or at least a direct sql call
|
||||||
|
|
||||||
|
|
||||||
//just in case no new tags are present which could mean a user removed all tags from a record so this
|
//just in case no new tags are present which could mean a user removed all tags from a record so this
|
||||||
//needs to proceed with the code below even if newTags is null as long as originalTags isn't also null
|
//needs to proceed with the code below even if newTags is null as long as originalTags isn't also null
|
||||||
if (newTags == null) newTags = new List<string>();
|
if (newTags == null) newTags = new List<string>();
|
||||||
@@ -147,41 +168,64 @@ namespace AyaNova.Biz
|
|||||||
addTags = newTags;
|
addTags = newTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region OLD SLOW METHOD FOR REFERENCE IN CASE CONCURRENCY EXCEPTIONS
|
||||||
|
// //ADD / INCREMENT TAGS
|
||||||
|
// //one by one method
|
||||||
|
// foreach (string s in addTags)
|
||||||
|
// {
|
||||||
|
// bool bDone = false;
|
||||||
|
// //Keep on trying until there is success
|
||||||
|
// //this allows for concurrency issues
|
||||||
|
// //I considered a circuit breaker / timeout here, but theoretically it should not be an issue
|
||||||
|
// //at some point it should not be a concurrency issue anymore
|
||||||
|
// do
|
||||||
|
// {
|
||||||
|
// //START: Get tag word and concurrency token and count
|
||||||
|
// var ExistingTag = await ct.Tag.FirstOrDefaultAsync(z => z.Name == s);
|
||||||
|
// //if not present, then add it with a count of 0
|
||||||
|
// if (ExistingTag == null)
|
||||||
|
// {
|
||||||
|
// await ct.Tag.AddAsync(new Tag() { Name = s, RefCount = 1 });
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// //Update the refcount
|
||||||
|
// ExistingTag.RefCount += 1;
|
||||||
|
// }
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// await ct.SaveChangesAsync();
|
||||||
|
// bDone = true;
|
||||||
|
// }
|
||||||
|
// catch (Exception ex) when (ex is DbUpdateConcurrencyException)//this allows for other types
|
||||||
|
// {
|
||||||
|
|
||||||
|
// Console.WriteLine("TagBiz::Exception udring update tags");
|
||||||
|
// //allow others to flow past
|
||||||
|
// //string sss = ex.ToString();
|
||||||
|
// }
|
||||||
|
// } while (bDone == false);
|
||||||
|
// }
|
||||||
|
|
||||||
|
#endregion old slow method
|
||||||
|
|
||||||
//ADD / INCREMENT TAGS
|
//ADD / INCREMENT TAGS
|
||||||
|
var existing = await ct.Tag.Where(x => addTags.Contains(x.Name)).ToListAsync();
|
||||||
foreach (string s in addTags)
|
foreach (string s in addTags)
|
||||||
{
|
{
|
||||||
bool bDone = false;
|
var t = existing.FirstOrDefault(x => x.Name == s);
|
||||||
//Keep on trying until there is success
|
if (t != null)
|
||||||
//this allows for concurrency issues
|
|
||||||
//I considered a circuit breaker / timeout here, but theoretically it should not be an issue
|
|
||||||
//at some point it should not be a concurrency issue anymore
|
|
||||||
do
|
|
||||||
{
|
{
|
||||||
//START: Get tag word and concurrency token and count
|
t.RefCount += 1;
|
||||||
var ExistingTag = await ct.Tag.FirstOrDefaultAsync(z => z.Name == s);
|
}
|
||||||
//if not present, then add it with a count of 0
|
else
|
||||||
if (ExistingTag == null)
|
{
|
||||||
{
|
ct.Tag.Add(new Tag() { Name = s, RefCount = 1 });
|
||||||
await ct.Tag.AddAsync(new Tag() { Name = s, RefCount = 1 });
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Update the refcount
|
|
||||||
ExistingTag.RefCount += 1;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await ct.SaveChangesAsync();
|
|
||||||
bDone = true;
|
|
||||||
}
|
|
||||||
catch (Exception ex) when (ex is DbUpdateConcurrencyException)//this allows for other types
|
|
||||||
{
|
|
||||||
//allow others to flow past
|
|
||||||
//string sss = ex.ToString();
|
|
||||||
}
|
|
||||||
} while (bDone == false);
|
|
||||||
}
|
}
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//DELETE TAGS
|
//DELETE TAGS
|
||||||
await ProcessDeleteTagsInRepositoryAsync(ct, deleteTags);
|
await ProcessDeleteTagsInRepositoryAsync(ct, deleteTags);
|
||||||
|
|||||||
Reference in New Issue
Block a user