i've a strange error in SQLite using a transaction, that i cannot figured out....
below there is my code:
_connection.RunInTransaction(() =>
{
_connection.UpdateAll(objProposte);
foreach (Proposte objProposta in objProposte)
{
string propostaID = objProposta.PropostaID;
List<ProposteDetails> lstProdDet = _connection.Table<ProposteDetails>().Where(x => x.PropostaID == propostaID).ToList();
if (lstProdDet != null && lstProdDet.Count() > 0)
{
//AN UPDATE GIVE ME THE SAME ERROR
_connection.DeleteAll(lstProdDet);
_connection.InsertAll(lstProdDet);
}
}
});
Seems that the _connection.UpdateAll(objProposte); works correctly, but when i try to do something else in the same transaction i got the following exception:
System.ArgumentException: savePoint is not valid, and should be the
result of a call to SaveTransactionPoint. Parameter name: savePoint
at SQLite.Net.SQLiteConnection.DoSavePointExecute (System.String
savePoint, System.String cmd) [0x00063] in
<8f2bb39aeff94a30a8628064be9c7efe>:0 at
SQLite.Net.SQLiteConnection.Release (System.String savepoint)
[0x00000] in <8f2bb39aeff94a30a8628064be9c7efe>:0 at
SQLite.Net.SQLiteConnection.RunInTransaction (System.Action action)
[0x0001d] in <8f2bb39aeff94a30a8628064be9c7efe>:0 at
SQLite.Net.SQLiteConnection.InsertAll (System.Collections.IEnumerable
objects, System.Boolean runInTransaction) [0x0001e] in
<8f2bb39aeff94a30a8628064be9c7efe>:0
Reading on the internet seems something related to a nested transaction, but is not my situation because is all done in the same transaction.
Thanks,
L-
edit 28-05-2018 12:16: That configuration works.... but should do the same things of the above :(
string my_transaction_point = null;
try
{
my_transaction_point = _connection.SaveTransactionPoint();
_connection.UpdateAll(objProposte, runInTransaction: false);
foreach (Proposte objProposta in objProposte)
{
string propostaID = objProposta.PropostaID;
List<ProposteDetails> lstProdDet = _connection.Table<ProposteDetails>().Where(x => x.PropostaID == propostaID).ToList();
if (lstProdDet != null && lstProdDet.Count() > 0)
{
_connection.DeleteAll(lstProdDet);
_connection.InsertAll(lstProdDet, runInTransaction: false);
}
}
_connection.Commit();
}
catch (Exception ex)
{
_connection.RollbackTo(my_transaction_point);
throw new Exception("UpdateProposta, " + ex.Message, ex);
}
.UpdateAll runs within its own transaction by default, you can turn that off by overriding the second parameter which defaults to true:
_connection.RunInTransaction(() =>
{
_connection.UpdateAll(objProposte, false);
// perform the rest of your CRUD operations
~~~
~~~
});
Related
I'm trying to run a transaction with realtime database to change an object, with the following code :
void tryBeHost()
{
string path = "weeks/" + week + "/games/" + gameId + "/gameInfo";
_dbRef.Child(path).RunTransaction(mutableData =>
{
GameInfo gameInfo = mutableData.Value as GameInfo;
if (gameInfo == null)
{
gameInfo = new GameInfo();
}
else if (gameInfo.host != null && gameInfo.host != myId)
{
return TransactionResult.Success(mutableData);
}
gameInfo.host = myId;
mutableData.Value = gameInfo;
return TransactionResult.Success(mutableData);
});
}
so I'm getting this weird error :
Exception in transaction delegate, aborting transaction
Firebase.Database.DatabaseException: Failed to parse object Serializables.GameInfo ---> System.ArgumentException: Invalid type Serializables.GameInfo for conversion to Variant
at Firebase.Variant.FromObject (System.Object o) [0x001df] in Z:\tmp\tmp.EaS8iXpRBh\firebase\app\client\unity\proxy\Variant.cs:117
at Firebase.Database.Internal.Utilities.MakeVariant (System.Object value) [0x00000] in Z:\tmp\tmp.sZ8vrpcx53\firebase\database\client\unity\proxy\Utilities.cs:25
--- End of inner exception stack trace ---
at Firebase.Database.Internal.Utilities.MakeVariant (System.Object value) [0x0000d] in Z:\tmp\tmp.sZ8vrpcx53\firebase\database\client\unity\proxy\Utilities.cs:27
at Firebase.Database.MutableData.set_Value (System.Object value) [0x00000] in Z:\tmp\tmp.sZ8vrpcx53\firebase\database\client\unity\proxy\MutableData.cs:136
at InternetShit.b__10_0 (Firebase.Database.MutableData mutableData) [0x00045] in /Users/sandukhan/Unity/projects/Ronda/Assets/Scripts/InternetShit.cs:73
at Firebase.Database.Internal.InternalTransactionHandler.DoTransaction (System.Int32 callbackId, System.IntPtr mutableData) [0x00022] in
Z:\tmp\tmp.sZ8vrpcx53\firebase\database\client\unity\proxy\InternalTransactionHandler.cs:49
UnityEngine.Debug:LogWarning(Object)
Firebase.Platform.FirebaseLogger:LogMessage(PlatformLogLevel, String) (at Z:/tmp/tmp.BbQyA8B710/firebase/app/client/unity/src/Unity/FirebaseLogger.cs:92)
Firebase.LogUtil:LogMessage(LogLevel, String) (at Z:/tmp/tmp.EaS8iXpRBh/firebase/app/client/unity/proxy/LogUtil.cs:68)
Firebase.Database.Internal.InternalTransactionHandler:DoTransaction(Int32, IntPtr) (at Z:/tmp/tmp.sZ8vrpcx53/firebase/database/client/unity/proxy/InternalTransactionHandler.cs:51)
Firebase.AppUtilPINVOKE:PollCallbacks()
Firebase.AppUtil:PollCallbacks() (at Z:/tmp/tmp.EaS8iXpRBh/firebase/app/client/unity/proxy/AppUtil.cs:32)
Firebase.Platform.FirebaseAppUtils:PollCallbacks() (at Z:/tmp/tmp.EaS8iXpRBh/firebase/app/client/unity/proxy/FirebaseAppUtils.cs:33)
Firebase.Platform.FirebaseHandler:Update() (at Z:/tmp/tmp.BbQyA8B710/firebase/app/client/unity/src/Unity/FirebaseHandler.cs:205)
Firebase.Platform.FirebaseMonoBehaviour:Update() (at Z:/tmp/tmp.BbQyA8B710/firebase/app/client/unity/src/Unity/FirebaseMonoBehaviour.cs:45)
my GameInfo Class is the following :
using System;
using Serializables;
namespace Serializables
{
[Serializable]
public class GameInfo
{
public string gameId;
public string host;
public string[] playersIds;
public string[] playersPics;
public string[] playersNames;
}
}
if anyone has an idea to solve this I will be grateful
I think you figured out the basics: You can only submit bool, string, long, double, IDictionary, and List<Object> to Value.
There's some interesting notes that I'd like to layer on top of this though! I like to use Unity's JsonUtility in conjunction with SetRawJsonValueAsync and GetRawJsonValue from GetValueAsync. Your code will look a bit like this:
async void SendGameInfo(string path, GameInfo info) {
try {
await _database
.GetReference(path)
.SetRawJsonValueAsync(JsonUtility.ToJson(info));
Debug.Log($"Successfully wrote {info}");
} catch (AggregateException e) {
Debug.LogWarning($"Failed with {e}");
}
}
async Task<GameInfo> ReadGameInfo(string path) {
try {
var dataSnapshot = await _database
.GetReference(path)
.GetValueAsync();
return JsonUtility.FromJson<GameInfo>(info);
} catch (AggregateException e) {
Debug.LogWarning($"Failed with {e}");
return null;
}
}
Also, if you can, having spent time digging through the library I like to consider IDictionary<string, object> the "basic primitive" of Realtime Database. This will be used as an intermediary format when you set RawJson if you step through the disassembly. Also, since Transactions don't provide access to raw json, this will give you a more uniform interface to RTDB (unless your data looks like an array, then your primitive is List<object> - "looks like" meaning that your keys are numbers and the range of numbers RTDB is aware of is about half full).
Of course, the team actively monitors the quickstart github page and you can use the new "feature request" template to request a change to any of this if it will help 😃.
I think RTFM always works, I found out that there are specific types that are accepted for mutableData.Value : bool, string, long, double, IDictionary and List{Object} where Object is one of previously listed types. So I got this error because my class GameInfo is not accepted and I have to convert my object to an IDictionary.
source : https://firebase.google.com/docs/reference/unity/class/firebase/database/mutable-data#class_firebase_1_1_database_1_1_mutable_data_1a4833f23246b3079078332d57c5649254
I have two related entities: User and UserProfile. A user can have many profiles (settings). I want to be able to update them together, but I am currently getting concurrency error when i do so:
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.
This is my code to update:
public void UpdateUser(UserList user, int timeoutMins)
{
using (var ctx = GetCodingContext())
{
try
{
ctx.Entry(user).State = System.Data.Entity.EntityState.Modified;
CR_USER_PROFILE timeoutProfile = GetTimeoutUserProfile(user.UserGUID);
if (timeoutProfile != null && !timeoutProfile.PROFILE_VALUE.Equals(timeoutMins.ToString()))
{
timeoutProfile.PROFILE_VALUE = timeoutMins.ToString();
UpdateUserProfile(timeoutProfile,ctx);
}
else if(timeoutProfile == null && timeoutMins > 0)
{
var timeoutKey = FFCEnumerations.Profiles.Keys.Timeout.GetStringValue();
AddUserProfile(user, timeoutKey, timeoutMins.ToString(), ctx);
}
ctx.SaveChanges();
}
catch (Exception ex)
{
throw new Exception("Error occurred updating user " + ex);
}
}
}
public void UpdateUserProfile(CR_USER_PROFILE profile, CodingContext ctx)
{
try
{
ctx.Entry(profile).State = System.Data.Entity.EntityState.Modified;
}
catch (Exception)
{
throw new Exception("Error occurred updating User Profile");
}
}
public CR_USER_PROFILE GetTimeoutUserProfile(Guid userGuid)
{
using (var ctx = GetCodingContext())
{
var timeoutKey = FFCEnumerations.Profiles.Keys.Timeout.GetStringValue();
var profileList = ctx.CR_USER_PROFILE.Where(p => p.UserGUID == userGuid && p.PROFILE_TYPE_CD == timeoutKey);
return profileList.SingleOrDefault();
}
}
It works well when I add both entities, but not when updating. Any ideas?
I think this is where there's a lot of discussion on this problem - Entity Framework: "Store update, insert, or delete statement affected an unexpected number of rows (0)."
I figured out that I was using a different context for fetching the profile I wanted to update. This was causing the concurrency conflict because EF thought this entity was being changed somewhere else (another context). So, I created an overload for this method so I can pass the context as an argument and fetch the entity with the same context I was going to update it with.
public CR_USER_PROFILE GetTimeoutUserProfile(Guid userGuid, CodingContext ctx)
{
var timeoutKey = FFCEnumerations.Profiles.Keys.Timeout.GetStringValue();
var profileList = ctx.CR_USER_PROFILE.Where(p => p.UserGUID == userGuid && p.PROFILE_TYPE_CD == timeoutKey);
return profileList.SingleOrDefault();
}
I'm having an issue trying to update the AspNetUsers database in MVC 5 with ASP.NET.
I'm trying to change a value in the user, Credits, which is stored in the database defined as Models.UserDB2.AspNetUsers.
However, when I attempt this, I get thrown this error.
A first chance exception of type 'System.Data.Entity.Core.EntityException' occurred in EntityFramework.SqlServer.dll
FATAL ERROR: An error occurred while starting a transaction on the provider connection. See the inner exception for details.`
Inner exception:
A first chance exception of type 'System.Data.Entity.Core.EntityException' occurred in EntityFramework.SqlServer.dll is the inner exception
This is the code causing the error.
if(player != null) {
foreach(Models.Item item in itemDB.Items) {
if(item.UserAssetOptionId == id) {
if(!item.Owner.ToLower().Equals(username.ToLower())) {
return Content("false");
} else {
item.Owner = "HomeguardDev";
item.InMarket = true;
player.Credits += item.Value / 10;
try {
itemDB.SaveChanges();
userDB2.SaveChanges();
return Content("true");
} catch(Exception e) {
Debug.WriteLine("FATAL ERROR: " + e.Message);
return Content("false");
}
}
}
}
}
The database updates fine if I only update the itemDB database, but I need to update the Credits value as well!
The model is updated with the latest schema with the database, so no problems there.
Anyone know what's up?
The problem is that you're trying to save the changes to itemDB while you're still iterating itemDB.Items, try to change your code to:
if (player != null) {
foreach(Models.Item item in itemDB.Items) {
if (item.UserAssetOptionId == id) {
if (!item.Owner.ToLower().Equals(username.ToLower())) {
return Content("false");
} else {
item.Owner = "HomeguardDev";
item.InMarket = true;
player.Credits += item.Value / 10;
break;
}
}
}
try {
itemDB.SaveChanges();
userDB2.SaveChanges();
return Content("true");
} catch (Exception e) {
Debug.WriteLine("FATAL ERROR: " + e.Message);
return Content("false");
}
}
As long as you're in the foreach, you can't start a second transaction.
I have this function which I use for my validation
public void Validate()
{
Action<List<Field>> validateFields = (field) =>
{
if (field != null && field.Any())
{
field.ForEach(x => x.Validate());
}
};
new List<List<Field>> { this.PersonElements, this.ContactElements, this.MiscElements }.ForEach(x =>
{
try
{
validateFields(x);
}
catch (Exception e)
{
Log.ErrorFormat("An exception has occurred while validation of {1} : {0}", e, x.ToString()); // print something as x.Name.ToString());
throw;
}
});
}
The problem is with this line:
new List<List<Field>> { this.PersonElements, this.ContactElements, this.MiscElements }.ForEach(x =>
I need to (in case an exception has occurred) log something as
An exception has occurred while trying to process "PersonElements / ContactElements / MiscElements" depending upon during which validation an exception has occurred.
How to get the list variable name in case of an exception occurring during its processing ?
Thanks in advance.
Variable names are only meaningful in their context. The same object could be accessed with different variables. So it is reasonable to provide the name of the collection in a type like a Tuple<String, List<Field>>.
new List<Tuple<List<Field>>> { Tuple.Create("PersonElements", this.PersonElements), ... }.ForEach (
...
validateFields(x.Item2);
...
Log.ErrorFormat("An exception has occurred while validation of {1} : {0}", e, x.Item1);
When an exception is thrown, how can I catch it and then continue execution starting from the line that caused the error?
EDIT:
Our program communicates with Indesign Server which crashes all the time and throws random COM related errors (these errors have to do with bugs in the Server itself). Indesign Server also takes a very long time to process commands so when it crashes, we want to avoid restarting execution. Instead, we want to continue from the line that caused the exception. Any line in the program can cause an exception. So technically, we cannot use a loop.
When an exception is thrown, how can I catch it and then continue execution starting from the line that caused the error? (Not the next line; retry the line that caused the exception.)
Do not try to do that. You're approaching this problem from the wrong direction.
The problem is that you have an unreliable subsystem. You have a desired policy for dealing with that unreliable subsystem, which is to retry the operation until it succeeds. If that's the case then don't put that logic in the line-of-business code which uses the subsystem. The line-of-business code should be about the business logic, not about the mechanism you choose to deal with the flaky subsystem. Isolate the mechanism to a specific class which makes the unreliable subsystem into a reliable subsystem.
That is, build a proxy class that has the same interface as the unreliable subsystem, and isolate your retry logic into that proxy class. Then the line-of-business code can use the proxy class as a reliable subsystem.
That said, a policy of "retry it until it works" is possibly a bad policy. If the subsystem is genuinely broken and not just flaky in some transient way, then "retry until it works" means "wait forever", and most users do not like waiting forever. For example, if the exception is a result of a router being unplugged rather than some transient condition then sitting there in a loop until someone plugs the router back in seems like a bad idea.
If you're looking for something general purpose then using a lambda would do the trick. For example
public static class Exception {
public static void Continue(Action action) {
try {
action();
} catch {
// Log
}
}
}
Exception.Continue(() => Statement1());
Exception.Continue(() => Statement2());
I wouldn't consider this an ideal solution though for large scale use. It causes an extra delegate allocation, delegate invocation and method invocation for every statement you use this on. Instead I would focus on identifying the functions which are causing you problems and add explicit wrappers for them individually.
You would have to surround any line that could throw an exception in its own try/catch block to accomplish that.
So instead of
try
{
StatementOne(); // Exception thrown here
StatementTwo();
}
catch (SOneException) { ... }
You would have to do:
try
{
StatementOne();
}
catch (SOneException) { ... }
StatementTwo();
If you need to retry an operation due to a (hopefully transient) exception, you can have a method like this:
public static class ExceptionHelper
{
public static void TryNTimesAndThenThrow(Action statement, int retryCount)
{
bool keepTrying = false;
do
{
try
{
statement();
keepTrying = false;
}
catch (Exception)
{
if (retryCount > 0)
{
keepTrying = true;
retryCount--;
}
else
{
// If it doesn't work here, assume it's broken and rethrow
throw;
}
}
} while (keepTrying)
}
}
Then you can just write:
ExceptionHelper.TryNTimesAndThenThrow(() => MightThrowATransientException(), 3);
Keep in mind both methods should be used sparingly. The former will clutter your code quite a bit, while the latter could end up taking a lot more time than you think (since its often a better idea to simply alert the user if something unexpected occurs. Thus the emphasis on a transient exception that you really do expect will disappear if you just try again.)
You could do something like this:
//Retry logic on opening the connection
int retries = 0;
openconnection:
try
{
connection.Open();
}
catch
{
retries++;
//Wait 2 seconds
System.Threading.Thread.Sleep(2000);
if (retries < MAXRETRIES)
{
goto openconnection;
}
else
{
throw;
}
}
I do not know about c#, but this is a javascript that can make a script run multiple times if there is an error.
try {
for (var i=0; (i < t && condition) || i === 0; i++) {
//t is how many times you want the code to run.
//condition should be true when there is an error.
//The code you want to execute multiple times if it fails.
}
} catch (e) {
//The code you want to execute if the code still runs into an error after being repeated multiple times.
}
For example, the following code simulates the case where you set a variable equal to the response from another server. And, the server responds on the 6th time the script is run.
try {
for (var i = 0; (i < 10 && typeof response === "undefined") || i === 0; i++) {
var response = (i === 5) ? 1 : undefined;
if (typeof response === "undefined") {console.log("ERROR!! #" + i)} else {console.log("Success!! #" + i)};
if (i === 9 && typeof response === "undefined") {throw new Error("Fail to get response from other server")};
}
} catch (e) {
console.log("Error Message: '" + e + "'");
}
You can run the above code to see the effect. The following is when another server never reply.
try {
for (var i = 0; (i < 10 && typeof response === "undefined") || i === 0; i++) {
var response = (i === -1) ? 1 : undefined;
if (typeof response === "undefined") {console.log("ERROR!! #" + i)} else {console.log("Success!! #" + i)};
if (i === 9 && typeof response === "undefined") {throw new Error("Fail to get response from other server")};
}
} catch (e) {
console.log("Error Message: '" + e + "'");
}
You can also make it into a function to use it easily.
function retry(code,times,condition,errorMessage) {
try {
for (var i = 0; (i < times && eval(condition)) || i === 0; i++) {
eval(code);
if (i === times-1 && eval(condition) && typeof errorMessage !== "undefined") {throw new Error(errorMessage)};
}
} catch (e) {
console.log("Error Message: '" + e + "'");
}
}
The first two example using the function.
function retry(code,times,condition,errorMessage) {
try {
for (var i = 0; (i < times && eval(condition)) || i === 0; i++) {
eval(code);
if (eval(condition)) {console.log("ERROR!! #" + i)} else {console.log("Success!! #" + i)};
if (i === times-1 && eval(condition) && typeof errorMessage !== "undefined") {throw new Error(errorMessage)};
}
} catch (e) {
console.log("Error Message: '" + e + "'");
}
}
retry("var response = (i === 5) ? 1 : undefined;",10,"typeof response === 'undefined'","Fail to get response from other server")
retry("var response = (i === -1) ? 1 : undefined;",10,"typeof response === 'undefined'","Fail to get response from other server")
I hope this helps some people.