I have this ASP.Net code and I was getting an error when running it. The error was:
Server: Msg 272, Level 16, State 1, Line 1 Cannot update a timestamp
column.
Here's the mapping for this table that I already have:
Property(x =>
x.Version).HasColumnName(#"Version").IsOptional().HasColumnType("timestamp").HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Computed);
Note that I do have a version column in my table.
public async Task<IHttpActionResult> Put([FromBody]WordForm wordForm)
{
// SampleSentences -> s
var oldsObj = db.SampleSentences
.Where(w => w.WordFormId == wordForm.WordFormId)
.AsNoTracking()
.ToList();
var newsObj = wordForm.SampleSentences.ToList();
// There is other code here to modify SampleSentences
//
//
// db.WordForms.Attach(wordForm);
// db.Entry(wordForm).State = EntityState.Modified;
wordForm.StatusId = (int)EStatus.Saved;
await db.SaveChangesAsync(User, DateTime.UtcNow);
return Ok(wordForm);
}
I was able to fix the error by adding comments to the two lines in the method. But could someone explain why I am getting the error if I don't comment out those lines. Should I not be able to Attach the wordForm and mark as Modified?
Your table probably has a rowversion or timestamp field which is used for optimistic concurrency. rowversion fields can't be set or updated at all. They are a value that gets incremented automatically each time a row is modified.
To avoid the problem, mark your rowversion property with the TimeStamp attribute:
[TimeStamp]
public byte[] RowVersion { get; set; }
In fact, timestamp is the deprecated name of the type which causes a bit of confusion
From the docs:
The timestamp syntax is deprecated. This feature will be removed in a future version of Microsoft SQL Server. Avoid using this feature in new development work, and plan to modify applications that currently use this feature.
Related
Below is the code that I have:
List<PatchOperation> patchOperations = new List<PatchOperation>();
patchOperations.Add(PatchOperation.Replace("/endpointId", 100));
string id = "id1";
PartitionKey partitionKey = new PartitionKey("partitionkey1");
await _container.PatchItemAsync<Watermark>(id,
partitionKey,
patchOperations);
I am expecting to get endpointId property to be replaced with 100.
However, I am faced with Message: {"Errors":["One of the specified inputs is invalid"]}.
May I check which part am I missing or do I have to wait for patch private preview feature to be enabled for my cosmos db?
For anyone else landing here looking for the reason why Cosmos might respond with One of the specified inputs is invalid for other request types, you may need to rename your Id property to id lower-cased or add an attribute:
[JsonProperty("id")]
public string Id { get; set; }
id attribite in cosmos db is case sensitive. Try to replace your id getter setter with "id" this case.
Even though error says "One of the specified inputs is invalid" you should see a status code 400 which indicates Bad Request. So Private Preview feature needs to be enabled for your account.
You can also enable Patch with your emulator by adding EnablePreview when you start the emulator,
.\CosmosDB.Emulator.exe /EnablePreview
In order to get it enabled you can register using this form and it should be done in 15-20 minutes. Let me know if that does not work
Another reason for this error message could be if you forget the forward slash before your property name:
await container.PatchItemAsync<T>(item.Id, new PartitionKey(item.PartitionKey), new[]
{
PatchOperation.Add("isDeleted", true),
PatchOperation.Add("ttl", DefaultTtl),
});
The correct code would be:
await container.PatchItemAsync<T>(item.Id, new PartitionKey(item.PartitionKey), new[]
{
PatchOperation.Add("/isDeleted", true),
PatchOperation.Add("/ttl", DefaultTtl),
});
I have a problem with my code, I wrote simple flutter app which is f note app, and I have included SQLite as a database , I run the app at first via the emulator and everything went cool , but when I tried to run it on my real device (which is an android device), the database did not respond (i.e I could not add new notes to the database ) and when I went back to run my app via the emulator .. the app did the same thing I found in my real device and in console I found this error
Error: Can't use 'SqfliteDatabaseException' because it is declared more than once.
I need help Please
I saw your code and the problem is that the exception you get is probably related to this one:
PlatformException(sqlite_error, UNIQUE constraint failed: Notetable.id
And it's because you need to manage the unicity of your primary key when you insert a new row. You can have a look at this SO question for a quick reference.
So just to quickly make your code working I've made this changes (please take this code only for reference, write a better one):
void createDataBase(Database db,int newVersion) async{
await db.execute('CREATE TABLE IF NOT EXISTS $noteTable ($col_id INTEGER PRIMARY KEY ,'+
'$col_title TEXT , $col_description TEXT , $col_date TEXT,$col_priority INTEGER)');
}
And
Future<int> insertData(myNote note)async{
var mdatabase = await database;
var _newNoteMap = note.convertToMap();
_newNoteMap['id'] = null;
var result = await mdatabase.insert(noteTable, _newNoteMap);
return result;
}
Pay attention that you always call a DB insert even when you update an existing note.
UPDATE: added additional modification (not listed before)
in databaseObject.dart
Map<String,dynamic> convertToMap(){
var mapObject = Map<String,dynamic>();
mapObject["id"] = _id;
mapObject["title"] = _title;
mapObject["description"] = _description;
mapObject["date"] = _date;
mapObject["priority"] = _priority;
return mapObject;
}
in Note.dart
if(res >= 1){
showAlertDialog('Status', "New note added successfully and the value of result is $res");
}
I have this function in my application. If the insert of Phrase fails then can someone tell me if the Audit entry still gets added? If that's the case then is there a way that I can package these into a single transaction that could be rolled back.
Also if it fails can I catch this and then still have the procedure exit with an exception?
[Route("Post")]
[ValidateModel]
public async Task<IHttpActionResult> Post([FromBody]Phrase phrase)
{
phrase.StatusId = (int)EStatus.Saved;
UpdateHepburn(phrase);
db.Phrases.Add(phrase);
var audit = new Audit()
{
Entity = (int)EEntity.Phrase,
Action = (int)EAudit.Insert,
Note = phrase.English,
UserId = userId,
Date = DateTime.UtcNow,
Id = phrase.PhraseId
};
db.Audits.Add(audit);
await db.SaveChangesAsync();
return Ok(phrase);
}
I have this function in my application. If the insert of Phrase fails
then can someone tell me if the Audit entry still gets added?
You have written your code in a correct way by calling await db.SaveChangesAsync(); only one time after doing all your modifications on the DbContext.
The answer to your question is: No, the Audit will not be added if Phrase fails.
Because you are calling await db.SaveChangesAsync(); after doing all your things with your entities, Entity Framework wil generate all the required SQL Queries and put them in a single SQL transaction which makes the whole queries as an atomic operation to your database. If one of the generated query e.g. Auditgenerated query failed then the transaction will be rolled back. So every modification that have been done to your database will be removed and so Entity Framework will let your database in a coherent state.
I use DocumentDB with the .NET SDK on a collection for which I set a custom indexing policy using IndexingMode.Lazy. This provides me eventual consistency for all operations on the collection.
I want to do upsert-like operation on non-critical data : I can afford duplicates and missed updates.
I use code like this :
public async Task UpsertChunk(MyChunk chunk)
{
var id = _documentClient
.CreateDocumentQuery<PersistentChunk>()
.Where(c => c.ChunkKey == chunk.ChunkKey)
.Select(c => c.id)
.FirstOrDefault();
var persistentChunk = chunk.ToPersistentChunk();
if (string.IsNullOrEmpty(id))
{
await _documentClient.CreateDocumentAsync(_collectionUri, persistentChunk);
}
else
{
persistentChunk.id = id;
var uri = UriFactory.CreateDocumentUri(_databaseId, _collectionId, id);
await _documentClient.ReplaceDocumentAsync(uri, persistentChunk);
}
}
I get, non-consistently, Conflict errors : Microsoft.Azure.Documents.DocumentClientException: Message: {"Errors":["Resource with specified id or name already exists"]}
However, since I use Automatic ID Generation, it should not be possible to have duplicate ids, even in the case of concurrent writes.
Has anyone encountered this kind of behavior before ?
I suspect this could be triggered by retries executed by the .NET SDK if successful writes are not acknowledged as such by DocumentDB.
Conflict errors : Microsoft.Azure.Documents.DocumentClientException: Message: {"Errors":["Resource with specified id or name already exists"]}
409 Conflict indicates the id provided for a resource on a PUT or POST operation has been taken by an existing resource. Please try to query documents by id property instead of ChunkKey property, and then you could determine to add/replace document based on the query result num.
var num = client.CreateDocumentQuery<MyChunk>("dbs/{your db}/colls/{your collection}").Where(c => c.id == chunk.id).AsEnumerable().Count();
I have one row in database to count total user logins
I have tried to increase number by getting the row and adding +1 to it
And i'm not sure about concurrency after I have tried this, counter was increased by 1 and not by 2 as it "should" (if many users will login at the same time)
using(var db = new Database()) {
db.Settings.FirstOrDefault(x => x.Name == "Logins").Counter++;
using(var db2 = new Database()) {
db2.Settings.FirstOrDefault(x => x.Name == "Logins").Counter++;
db2.SaveChanges();
}
db.SaveChanges();
}
Why not make a single table for storing the number of people who have logged in increment the field when someone logs in successfully and decrease when the user logs out. For example for login:
_Users = context.Users.First(aa => aa.UserName.ToUpper() == _UserName.ToUpper() && aa.MDesktop == true);
if (_Users != null)
{
context.LogEntry.FirstOrDefault().Counter++;
context.SaveChanges();
}
This is old but it is still a relevant discussion for new EF developers and it deserves an explanation.
OP's example uses two different DBContext's, effectively OP has defined two different units of work, and importantly, neither of these is aware that the other exists at all.
Lets assume that the current value of the "Logins" setting is 5
For the purposes of this walkthrough lets save the two instances that are requested from Settings into variables outside of the scope of the DB contexts in question:
Setting setting1 = null;
Setting setting2 = null;
using(var db = new Database()) {
// DB: 5, Setting1: null, Setting2: null
// Load the value of setting1 from the database
setting1 = db.Settings.FirstOrDefault(x => x.Name == "Logins");
// DB: 5, Setting1: 5, Setting2: null
// Increment the value of setting1
setting1.Counter++;
// at this point, no changes have been saved yet, the DB still holds the original value for "Logins"
// DB: 5, Setting1: 6, Setting2: null
// Create a new context called DB2
using(var db2 = new Database()) {
// load setting2 from the DB
setting2 = db2.Settings.FirstOrDefault(x => x.Name == "Logins");
// right now setting2 still has a value of 5, the previous change was not yet committed
// DB: 5, Setting1: 6, Setting2: 5
setting2.Counter++;
// DB: 5, Setting1: 6, Setting2: 6
// Save the value of Setting2 back to the database
db2.SaveChanges();
// DB: 6, Setting1: 6, Setting2: 6
// At this point setting1, setting2, and the DB all agree the value is 6.
}
// The context is only aware that we previously set the value of setting1 to 6
// so it issues an update to the DB
db.SaveChanges();
// ultimately this update would not actually change anything.
}
Entity Framework, Unit of Work and Repository data access patterns all exhibit this behaviour, when you create a new DbContext IRepository or IUnitOfWork it is done so in isolation of any others that might exist at the same point in time, there is no difference between instantiating a new context in the same method, or a different thread or even executing on entirely different servers. If you need to implement counters or incremental values there is always a degree of uncertainty when we first cache the value of the field, then increment the value and later write that value back to the database.
To minimise the potential conflict, read the record and save it immediately after, then as a rule always re-query the value of this setting before you use it.
You can call .SaveChanges() multiple times in your logic, in this example simply saving before instantiating the second context, or at least before the second context loaded the record from the DB would have been enough to see the value incremented twice:
using(var db = new Database()) {
db.Settings.FirstOrDefault(x => x.Name == "Logins").Counter++;
db.SaveChanges(); // save it back as soon as we've made the change
using(var db2 = new Database()) {
db2.Settings.FirstOrDefault(x => x.Name == "Logins").Counter++;
db2.SaveChanges();
}
db.SaveChanges();
}
Where possible, you will find the code simpler if you can avoid a schema where an incrementing or counter fields is required, instead you could turn the count logic into a query based solution.
Counters are of course a special case, you could always make direct SQL calls to the database, both for read or increment to ensure that that we bypass any potential caching that might occur with the records through EF.
You could do this as a one liner to increment the value:
dbContext.Database.ExecuteSqlCommand("UPDATE Setting SET[Counter] = IsNull([Counter],0) + 1 WHERE[Name] = 'Logins'");
Or if you want to inspect the new value:
int newCount = dbContext.Database.SqlQuery<int>(#"
UPDATE Setting SET[Counter] = IsNull([Counter],0) + 1
OUTPUT inserted.[Counter]
WHERE [Name] = 'Logins'").First();
If you need to ge tthe current value, and know that it is the most up-to-date then you can simply query it from any context in the same way:
int logins = dbContext.Database.SqlQuery<int>(#"
SELECT [Counter] FROM Setting
WHERE [Name] = 'Logins'").First();
I hope this sheds some light on why your code only incremented the value once, its not a fault in EF, just something that we need to be aware of, once EF has read values form the DB, they are potentially already stale or out of date. If optimistic concurrency is not appropriate for your use case, then you will need to think outside of the box a little bit ;)
the easy approach?
then I'd suggest using a manual transaction in EF Core
ef core transaction docs
Be sure to add an unique constraint of some sort eb. (settings id + logins counter)
using(var transaction = _context.Database.BeginTransaction())
{
try
{
var totalLoginsCounter = _context.Settings.FirstOrDefault(x => x.Name == "Logins").Counter;
totalLoginsCounter += 1;
await _context.SaveChanges();
transaction.Commit();
}
catch
{
commit.RollBack();
}
}
should concurrency happen the request will fail. Because it would try to put duplicate keys which is not possible. then HIGHLY recommend you'd then implement a retry pattern to avoid people not being able to actually login because a number in your database didn't get updated.