का उपयोग करता है मेरे पास टाइमस्टैम्प (कंसुरेंसी टोकन) कॉलम वाला मॉडल है। मैं एक एकीकरण परीक्षण लिखने की कोशिश कर रहा हूं जहां मैं जांच करता हूं कि यह काम करता है जैसा कि मैं उम्मीद करता हूं लेकिन बिना किसी सफलता के। मेरा परीक्षणईएफ कोर - सहेजने से पहले टाइमस्टैम्प सेट करें अभी भी पुराने मान
- उस इकाई को प्राप्त करें जिसे वेब एपीआई से एचटीपी क्लाइंट कॉल के साथ अपडेट किया जाना चाहिए।
- प्रसंग को सीधे अनुरोध करें और एक ही इकाई
- बदलें इकाई पर एक संपत्ति कदम 2.
- सहेजें इकाई चरण में अद्यतन 3.
- बदलें कदम से इकाई पर एक संपत्ति से प्राप्त 1.
- वेब एपीआई के लिए एचटीपी क्लाइंट के साथ नई इकाई के साथ एक पुट अनुरोध भेजें।
- मेरे वेब एपीआई में मुझे पहले डेटाबेस से इकाई मिलती है, ग्राहक से प्राप्त संपत्ति से संपत्ति और टाइमस्टैम्प मान सेट करता है। अब एपीआई नियंत्रक में मेरी इकाई ऑब्जेक्ट में डेटाबेस में से एक की तुलना में एक अलग टाइमस्टैम्प मान है। अब मैं उम्मीद करता हूं कि savechanges असफल हो जाएंगे, लेकिन ऐसा नहीं है। इसके बजाय यह इकाई को डेटाबेस में सहेजता है और एक नया टाइमस्टैम्प मान उत्पन्न करता है। मैंने जेनरेट क्वेरी देखने के लिए एसक्यूएल सर्वर प्रोफाइलर के साथ जांच की और यह पता चला कि अभी भी पुराने टाइमस्टैम्प मान का उपयोग किया गया है, न कि मैंने अपने एपीआई नियंत्रक में इकाई को सौंपा है।
इसका कारण क्या है? क्या टाइमस्टैम्प के साथ डेटाबेस उत्पन्न मूल्य होने के साथ इसका कोई संबंध नहीं है जो ईएफ को व्यापार परत से किए गए परिवर्तनों को अनदेखा करता है?
पूर्ण परीक्षण आवेदन यहां पाया जा सकता: https://github.com/Abrissirba/EfTimestampBug
public class BaseModel
{
[Timestamp]
public byte[] Timestamp { get; set; }
}
public class Person : BaseModel
{
public int Id { get; set; }
public String Title { get; set; }
}
public class Context : DbContext
{
public Context()
{}
public Context(DbContextOptions options) : base(options)
{}
public DbSet<Person> Persons{ get; set; }
}
protected override void BuildModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "7.0.0-rc1-16348")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("EFTimestampBug.Models.Person", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<byte[]>("Timestamp")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate();
b.Property<string>("Title");
b.HasKey("Id");
});
}
// PUT api/values/5
[HttpPut("{id}")]
public Person Put(int id, [FromBody]Person personDTO)
{
// 7
var person = db.Persons.SingleOrDefault(x => x.Id == id);
person.Title = personDTO.Title;
person.Timestamp = personDTO.Timestamp;
db.SaveChanges();
return person;
}
[Fact]
public async Task Fail_When_Timestamp_Differs()
{
using (var client = server.CreateClient().AcceptJson())
{
await client.PostAsJsonAsync(ApiEndpoint, Persons[0]);
// 1
var getResponse = await client.GetAsync(ApiEndpoint);
var fetched = await getResponse.Content.ReadAsJsonAsync<List<Person>>();
Assert.True(getResponse.IsSuccessStatusCode);
Assert.NotEmpty(fetched);
var person = fetched.First();
// 2
var fromDb = await db.Persons.SingleOrDefaultAsync(x => x.Id == person.Id);
// 3
fromDb.Title = "In between";
// 4
await db.SaveChangesAsync();
// 5
person.Title = "After - should fail";
// 6
var postResponse = await client.PutAsJsonAsync(ApiEndpoint + person.Id, person);
var created = await postResponse.Content.ReadAsJsonAsync<Person>();
Assert.False(postResponse.IsSuccessStatusCode);
}
}
// generated sql - @p1 has the original timestamp from the entity and not the assigned and therefore the save succeed which was not intended
exec sp_executesql N'SET NOCOUNT OFF;
UPDATE[Person] SET[Title] = @p2
OUTPUT INSERTED.[Timestamp]
WHERE [Id] = @p0 AND[Timestamp] = @p1;
',N'@p0 int,@p1 varbinary(8),@p2 nvarchar(4000)',@p0=21,@p1=0x00000000000007F4,@p2=N'After - should fail'
कृपया अपनी इकाई फ्रेमवर्क मैपिंग कोड शामिल करें, शायद इसके साथ कुछ करने के लिए कुछ है। क्या आप एसक्यूएल प्रोफाइलर से उत्पन्न और निष्पादित क्वेरी भी पोस्ट कर सकते हैं? – Igor