Skip to content

Commit b12039a

Browse files
authored
Added AppendStoredProcedureCall (#1185)
1 parent b141e0a commit b12039a

4 files changed

Lines changed: 989 additions & 0 deletions

File tree

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
* The contents of this file are subject to the Initial
3+
* Developer's Public License Version 1.0 (the "License");
4+
* you may not use this file except in compliance with the
5+
* License. You may obtain a copy of the License at
6+
* https://github.com/FirebirdSQL/NETProvider/raw/master/license.txt.
7+
*
8+
* Software distributed under the License is distributed on
9+
* an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
10+
* express or implied. See the License for the specific
11+
* language governing rights and limitations under the License.
12+
*
13+
* All Rights Reserved.
14+
*/
15+
16+
//$Authors = Jiri Cincura (jiri@cincura.net)
17+
18+
using System;
19+
using System.Linq;
20+
using System.Threading.Tasks;
21+
using Microsoft.EntityFrameworkCore;
22+
using NUnit.Framework;
23+
24+
namespace FirebirdSql.EntityFrameworkCore.Firebird.Tests.EndToEnd;
25+
26+
public class DeleteTestsUsingSP : EntityFrameworkCoreTestsBase
27+
{
28+
class DeleteContext : FbTestDbContext
29+
{
30+
public DeleteContext(string connectionString)
31+
: base(connectionString)
32+
{ }
33+
34+
protected override void OnTestModelCreating(ModelBuilder modelBuilder)
35+
{
36+
base.OnTestModelCreating(modelBuilder);
37+
38+
var insertEntityConf = modelBuilder.Entity<DeleteEntity>();
39+
insertEntityConf.Property(x => x.Id).HasColumnName("ID");
40+
insertEntityConf.Property(x => x.Name).HasColumnName("NAME");
41+
insertEntityConf.ToTable("TEST_DELETE_USP");
42+
modelBuilder.Entity<DeleteEntity>().DeleteUsingStoredProcedure("SP_TEST_DELETE",
43+
storedProcedureBuilder =>
44+
{
45+
storedProcedureBuilder.HasOriginalValueParameter(x => x.Id);
46+
storedProcedureBuilder.HasRowsAffectedResultColumn();
47+
});
48+
}
49+
}
50+
class DeleteEntity
51+
{
52+
public int Id { get; set; }
53+
public string Name { get; set; }
54+
}
55+
[Test]
56+
public async Task Delete()
57+
{
58+
await using (var db = await GetDbContext<DeleteContext>())
59+
{
60+
await db.Database.ExecuteSqlRawAsync("create table test_delete_usp (id int primary key, name varchar(20))");
61+
await db.Database.ExecuteSqlRawAsync("insert into test_delete_usp values (65, 'test')");
62+
await db.Database.ExecuteSqlRawAsync("insert into test_delete_usp values (66, 'test')");
63+
await db.Database.ExecuteSqlRawAsync("insert into test_delete_usp values (67, 'test')");
64+
var sp = """
65+
create procedure sp_test_delete (
66+
pid integer)
67+
returns (rowcount integer)
68+
as
69+
begin
70+
delete from test_delete_usp
71+
where id = :pid;
72+
rowcount = row_count;
73+
suspend;
74+
end
75+
""";
76+
await db.Database.ExecuteSqlRawAsync(sp);
77+
var entity = new DeleteEntity() { Id = 66 };
78+
var entry = db.Attach(entity);
79+
entry.State = EntityState.Deleted;
80+
await db.SaveChangesAsync();
81+
var values = await db.Set<DeleteEntity>()
82+
.FromSqlRaw("select * from test_delete_usp")
83+
.AsNoTracking()
84+
.OrderBy(x => x.Id)
85+
.ToListAsync();
86+
Assert.AreEqual(2, values.Count());
87+
Assert.AreEqual(65, values[0].Id);
88+
Assert.AreEqual(67, values[1].Id);
89+
}
90+
}
91+
92+
class ConcurrencyDeleteContext : FbTestDbContext
93+
{
94+
public ConcurrencyDeleteContext(string connectionString)
95+
: base(connectionString)
96+
{ }
97+
98+
protected override void OnTestModelCreating(ModelBuilder modelBuilder)
99+
{
100+
base.OnTestModelCreating(modelBuilder);
101+
102+
var insertEntityConf = modelBuilder.Entity<ConcurrencyDeleteEntity>();
103+
insertEntityConf.Property(x => x.Id).HasColumnName("ID");
104+
insertEntityConf.Property(x => x.Name).HasColumnName("NAME");
105+
insertEntityConf.Property(x => x.Stamp).HasColumnName("STAMP")
106+
.IsRowVersion();
107+
insertEntityConf.ToTable("TEST_DELETE_CONCURRENCY_USP");
108+
modelBuilder.Entity<ConcurrencyDeleteEntity>().DeleteUsingStoredProcedure("SP_TEST_DELETE_CONCURRENCY",
109+
storedProcedureBuilder =>
110+
{
111+
storedProcedureBuilder.HasOriginalValueParameter(x => x.Id);
112+
storedProcedureBuilder.HasOriginalValueParameter(x => x.Stamp);
113+
storedProcedureBuilder.HasRowsAffectedResultColumn();
114+
});
115+
}
116+
}
117+
class ConcurrencyDeleteEntity
118+
{
119+
public int Id { get; set; }
120+
public string Name { get; set; }
121+
public DateTime Stamp { get; set; }
122+
}
123+
[Test]
124+
public async Task ConcurrencyDelete()
125+
{
126+
await using (var db = await GetDbContext<ConcurrencyDeleteContext>())
127+
{
128+
await db.Database.ExecuteSqlRawAsync("create table test_delete_concurrency_usp (id int primary key, name varchar(20), stamp timestamp)");
129+
await db.Database.ExecuteSqlRawAsync("insert into test_delete_concurrency_usp values (65, 'test', current_timestamp)");
130+
await db.Database.ExecuteSqlRawAsync("insert into test_delete_concurrency_usp values (66, 'test', current_timestamp)");
131+
await db.Database.ExecuteSqlRawAsync("insert into test_delete_concurrency_usp values (67, 'test', current_timestamp)");
132+
var sp = """
133+
create procedure sp_test_delete_concurrency (
134+
pid integer,
135+
pstamp timestamp)
136+
returns (rowcount integer)
137+
as
138+
begin
139+
delete from test_delete_concurrency_usp
140+
where id = :pid and stamp = :pstamp;
141+
rowcount = row_count;
142+
if (rowcount > 0) then
143+
suspend;
144+
end
145+
""";
146+
await db.Database.ExecuteSqlRawAsync(sp);
147+
var entity = new ConcurrencyDeleteEntity() { Id = 66, Stamp = new DateTime(1970, 1, 1) };
148+
var entry = db.Attach(entity);
149+
entry.State = EntityState.Deleted;
150+
Assert.ThrowsAsync<DbUpdateConcurrencyException>(() => db.SaveChangesAsync());
151+
}
152+
}
153+
}

0 commit comments

Comments
 (0)