EFcore update context after _dbContext.Database.ExecuteSqlCommand - c#

I am working with SQL Geography types and since EFCore doesn't support type geography, I need to execute a stored proc to update my entity.
This works well, but when I call the row that has just been updated, Its returning the old value.
I can see the correct value in the DB.
Here is some of my code:
var FlId = new SqlParameter("#flId", SqlDbType.Int) { Value = testModel.Flid ?? SqlInt32.Null };
var OID = new SqlParameter("#oID", SqlDbType.Int) { Value = testModel.OID };
var flName = new SqlParameter("#flName", SqlDbType.VarChar) { Value = testModel.FLName };
var result = _dbContext.Database.ExecuteSqlCommand("testSchema.spUpsertOFL #flId, #oID, #flName,
parameters: new[] { FlId , OID , flName });
The above code works and performs the upsert, but when I then run a regular query using EFCore, I get back a stale record.
var TestQuery = _dbContext.Fl.FirstOrDefault(x => x.FLCode == testModel.Flcode && x.OID == testModel.OID);
I tried reloading the context with:
_dbContext.Entry(fl).Reload();
But I got an error saying :
"There was an internal error creating the Fl. The instance of entity type 'Fl' cannot be tracked because another instance with the same key value for {'FlID'} is already being tracked."
If I use postman and hit my get endpoint - the correct result returns.

Try detaching the entry and then requerying
dbContext.Entry(entry).State = EntityState.Detached;
entry = context.Users.FirstOrDefault(e => e.Id == id);
While researching this problem I saw many people say the best course of action is to dispose and create a new dbcontext https://github.com/dotnet/efcore/issues/16861
The next most common solution I saw was to call dbContext.Entry(entry).Reload(); This did not work for me because I was changing the type of an entry via a discriminator. Detaching the old entry means when you request it again it pulls from the database instead of the local cache

Related

LiteDb collection returns invalid data when searching by id

I try to find entity by its id in liteDb. But the wrong entity returns as a result (with another id).
It's reproduced only on one entity for one client (other clients works good).
I use LiteDB 4.1.2.0 in my program, also I tried to find the entity in LiteDBViewer (4.1.0.0).
var id = Guid.Parse("9fe943d3-97d4-4301-8279-eca89b4209ee");
var order = dbOrders.FindById(id);
//dbOrders is LiteCollection<Order>
I expect that liteDb will return entity with my id (9fe943d3-97d4-4301-8279-eca89b4209ee), but the actual output entity with id = 2aba5886-ca30-4d67-9cf8-558441ef5eb6.
The result of liteDbViewer: https://i.ibb.co/WntgmZK/2019-08-16-1230.png
Welcome to the community!
Based on the information you provided, you could try the following:
var id = Guid.Parse("9fe943d3-97d4-4301-8279-eca89b4209ee");
var order = dbOrders.FindOne(o => o.Id == id);
Make sure that the attribute "Id" in column "Orders" is of type "guid". If it isn't, either make it one, or use id.ToString() if it's of string type.
https://github.com/mbdavid/LiteDB/wiki/Queries
EDIT:
static void Main(string[] args)
{
// Open database (or create if not exits)
using (var db = new LiteDatabase(#"MyData.db"))
{
// Get user collection
var users = db.GetCollection<User>("users");
var id = Guid.Parse("8dd0997e-42b1-432d-820e-4637dd08fa2e");
//var userById = users.FindOne(x => x.Id == id);
var userById = users.FindById(id);
if (id != userById.Id)
Console.WriteLine("Error!");
}
}
I'm not sure where the problem could be. The above code works as expected. The only difference is that you're using LiteCollection<Order> instead of GetCollection<Order>. Could you share its structure in your main post?

Teradata Query Using Dapper C#

I am trying to dynamically query a Teradata database using Dapper but am having some issues. Here is the code:
// model variable is the parameter passed in with search information
using (IDbConnection con = new TdConnection(connection.GetConnectionString()))
{
var builder = new SqlBuilder();
var selector = builder.AddTemplate($"SELECT * FROM Temp_Table /**where**/");
if (model.Id != 0)
{
builder.Where("Id = ?", new { model.Id });
}
if (!string.IsNullOrEmpty(model.Employee_Id))
{
builder.Where("Employee_Id = ?", new { model.Employee_Id });
}
var data= con.Query<TableModel>(selector.RawSql, model).ToList();
return data;
}
The error I am getting is:
[Teradata Database] [3939] There is a mismatch between the number of
parameters specified and the number of parameters required.
I have used very similar code to query DB2 which worked just fine; what do I need to do differently with Teradata?
Managed to figure it out. Changed the line for getting the data to:
var data= con.Query<TableModel>(selector.RawSql, selector.Parameters).ToList();
Not sure why passing in the model worked just fine in my DB2 version but not this Teradata version.
At first glance it appears to be falling through and not adding any "where" condition. Try to structure it in such a way that if it falls through then add 1=1 or a Teradata equivalent if that doesn't work.
I'm unfamiliar with the SqlBuilder() class; but if you have a way of seeing if there aren't any Where constraints added, then to add a generic one. Or, a dirtier way would be to keep a bool reference and check at the end.
Update
Try passing in the parameters:
var data= con.Query<TableModel>(selector.RawSql, selector.Parameters).ToList();

Cannot add an entity with a key that is already in use update operation

I am creating small application in which i have used LINQ To SQL to perform all operation to database.
Now here i am giving the small part of my database structure please take a look.
So update language detail i am getting the object of login using the datacontext something like this.
XVDataContext Context = new XVDataContext ();
var myQuery = from objLogIn in Context.GetTable<LogIn>() where objLogIn.Emp_Id == nEmpId select objLogIn;
In nEmpId i will always have some value.
So it is not creating any problem in fact i am getting the required record from DB and storing it in objUser object using the following code.
LogIn objUser = myQuery.First<LogIn>();
Now to update LanguageDetail i am executing following code but it throws Exception when i execute SubmitChanges line.
Here is the code that i am executing to update.
LanguageDetail obj = new LanguageDetail();
foreach (string sLanguages in TextBoxLanguagesKnown.Text.Split('\n'))
{
obj.Emp_Id = objUser.Emp_Id;
obj.Language = sLanguages.Trim();
}
objUser.LanguageDetails[0] = obj;
Context.SubmitChanges();
I already read following links.
cannot add an entity with a key that is already in use
LINQ To SQL exception with Attach(): Cannot add an entity with a key that is alredy in use
Cannot add an entity with a key that is already in use (LINQ)
By reading the above links i found that i am doing some mistake in ID fields but still i am unable to resolve.
Please tell me the clear understanding of raising this issue and how can i resolve this.
EDIT:
I simply want to update LanguageDetail table.
When i try to add new object using following code it still throws exception.
objUser.LanguageDetail.Add(obj);
You might want to add / remove languages for specific user by using following code.
var languages = TextBoxLanguagesKnown.Text.Split('\n');
// Removes deleted languages (first find all language details that are missing from the UI).
var deletedLanguages = objUser.LanguageDetails.Where(ld => !languages
.Any(l => ld.Language == l.Trim())).ToArray();
foreach(var deletedLanguage in deletedLanguages)
{
objUser.LanguageDetails.Remove(deletedLanguage);
Context.LanguageDetails.DeleteOnSubmit(deletedLanguage);
}
// Adds new languages (then adds new language details that are not found in the database).
var newLanguages = languages.Where(l => !objUser.LanguageDetails
.Any(ld => ld.Language == l.Trim())).ToArray();
foreach (string newLanguage in newLanguages)
{
var languageDetail = new LanguageDetail
{
Emp_Id = objUser.Emp_Id,
Language = newLanguage.Trim()
};
objUser.LanguageDetails.Add(languageDetail);
}
Context.SubmitChanges();
From my understanding you want to update the LanguageDetail entity in your database. In order to do so you have to do one of the following:
Retrieve the original LanguageDetail object based on its id, and update that object instead of creating a new one and assigning it the id of an existing object.
Attach the newly created object to your context instead of just giving a reference to it to your LanguageDetails collection.
The exception you are seeing happens because the way linq to sql behaves is that it threats the obj as a new object that you want to insert and because of that it tries to insert it into the language details table.
Modifying your code like that should work:
Context.LanguageDetails.Attach(obj);
objUser.Employee_LanguageDetails[0] = obj;

Why entity framework isn't implement identity map with unit of work?

I have written test code as bellow:
Entities db = new Entities();
var place = new Place
{
Id = Guid.NewGuid(),
Name = "test",
Address = "address"
};
db.Places.Add(place);
var cachedPlace = db.Places.Where(x => x.Id == place.Id).FirstOrDefault(); \\ null
I expected dbset will return the added entity. But it gives me object only after changes were saved to the real DB.
If you want to access the unsaved query, then you use the Local property of the DbSet.
The reason it doesn't work the way you want is that it must also support autonumbered identities, and that will mean the ID is 0. If you insert multiple records, you would have multiple objects with the same 0 ID. EF won't know what the real ID is until after it's been saved.

C# LINQ-TO-SQL: datacontext.ChangeConflicts not showing all conflicts

I am running into problem when working with linq-to-sql and trying to resolve conflicts. The problem is that sometimes conflict is not detected. Please look on the below code sample:
// Setup the object to re-produce the problem
//
// MyObject has properties: id, my_string, my_int and version (timestamp) to enable
// conflicts detection
var context = new MyDataContext();
var obj = new MyObject();
obj.id = "1";
obj.my_string = "value";
obj.my_int = 0;
context.MyTable.InsertOnSubmit(obj);
context.SubmitChanges();
context.dispose();
// Get 2 data contexts
var context1 = new MyDataContext();
var context2 = new MyDataContext();
// Get 2 instances of obj - 1 from each context
var obj1= context1.MyTable.SingleOrDefault(o => o.id == "1");
var obj2= context2.MyTable.SingleOrDefault(o => o.id == "1");
// Change the values of obj1 and update it to the database
obj1.my_string= "value1";
obj1.my_int= 0;
context1.SubmitChanges();
context1.Dispose();
// Update the changes in obj2
obj2.my_string= "value2";
obj2.my_int= 1;
// Now the database contains:
// id: "1"
// my_string: "value1"
// my_int: 0
// obj2 contains:
// id: "1"
// my_string: "value2"
// my_int: 1
try
{
context2.SubmitChanges();
}
catch(ChangeConflictException ex)
{
LogInfo("Conflicting members:");
context2.ChangeConflicts[0].MemberConflicts.ToList().ForEach(
mcc=>LogInfo("Property '{0}': Database value: {1}, Current Value:{2}", mcc.Member.Name, mcc.DatabaseValue, mcc.CurrentValue)
);
}
context2.Dispose();
In the catch I expects to see 3 member conflicts: my_string, my_int and version but I see only 2 member conflicts: my_string and version. The my_int is not detected as conflict.
If I would have setup the my_int when the object was created to value different from the value that I have assigned to obj2, the conflict is being detected.
I found some commonality: when the value of a property (any property) of the original object is equal to the value of obj1, the conflict for this property is not detected.
I would like to get any idea how to overcome this problem so conflicts will successfully detected
I found the root cause for the problem. The reason that I didn't get conflict for "my_int" property is because it is not conflicting with the database value that context2 "knows". I'll explain this:
I thought that conflict is defined as when the values of the object that being saved are not equal to the values in the database. IT'S NOT!!!!
Conflict is defined as when the current value in the database is not equal to the original value that the context familiar. The original value is the value that was in the database when the select query executed.
Examining the example in my question, when context2 selected the data from the database to create obj2, the proeprties values were: my_string: "value", my_int:0. Those are the original values.
When I tried to save the data, LINQ-TO-SQL compared the original values to the database values. The database values at this time (after obj1 was saved): my_string: "value1", my_int:0
As a result, I got conflict in my_string (original: "value", database:"value1") but not in my_int(original: 0, database: 0).
Discovering this helped me to understand why there is no conflict, but still it didn't helped me to solve the problem because the wrong value was not saved to the database because I analyzed only the MemberChangeConflicts that exists in the ObjectChangeConflict and if the property that I am checking is not exists there, the check logic is skipped.
The solution was to analyze also the modified members enumerable that give access to all the properties that were modified and for each one I can get the original value and the new value.
To get the modified members, there is a need to execute method called GetModifiedMembers. This method is in the table of the object data type and can be executed as follows:
var mmc = context.MyTable.GetModifiedMembers(myobject);
Combining the conflicts and modified members gave me the full picutre of what happened to the object that caused the conflict and handle it properly.
Thanks for Damien_The_Unbeliever for giving the hint in his comment.

Categories

Resources