AIFA AKADEMI · Kurumsal Wiki
4.310 makale
Ana sayfa Kurumsal Wiki › Kod mimarisi › ARCH-DB-ACCESS

Veri Erişim Katmanı

ARCHITECTUREERPGOLD_COREACTIVEHIGHSOURCE_IMPLEMENTED
Kanonik kaynak: markdown · bu sayfa üretilmiş görünümdür

Kaynak: docs/knowledge-base/architecture/database-access.md — KB-docs (tam çözüm taraması, 1.029 .vb).

Veri Erişim Mimarisi (ERPPRO → ERPGOLDV2)

Gerçeklik türü: AS-IMPLEMENTED (kod). Depo kökü: /home/claude/erppro/ERPPRO; yollar ERPPRO/... ve DijitalFatura/... projeye görelidir. Sayısal değerler kb_src/erppro/scan.json (statik tarama; 1029 dosya, 3426 sınıf, 16043 metot, 1468 SQL ifadesi) üzerinden Python ile üretildi; kod satırları grep ile doğrulandı. Gizlilik: bağlantı dizesi, parola, lisans ve sunucu adresi değerleri kopyalanmamıştır; yalnız yapı ve konum verilir. İlgili dokümanlar: hotspots (büyüklük/çiftleme/boş catch tabloları), technical-debt (borç kaydı), application-flow.

1. Özet

Uygulama tek bir SQL Server veritabanına (ERPGOLDV2) doğrudan ADO.NET (System.Data.SqlClient) ile bağlanır; ORM yoktur. Üç farklı erişim yolu bir arada yaşar:

Yol Nerede Kaç sınıf kullanıyor Etiket
Elle yazılmış SQL metni + InternalDb sarmalayıcısı ERPPRO/Class/Module/InternalDb.vb 293 sınıf InternalDb kullanıyor; 364 sınıfta SQL ifadesi var (scan uses/sql_count) VERIFIED
Doğrudan SqlConnection/SqlCommand (sarmalayıcısız) 39 dosya, 129 satır (grep "New SqlClient.SqlConnection") — özellikle Class/Ext/*, Sistem.KurListesi, DijitalFatura/Common/ERPFatura.vb 39 dosya VERIFIED
Attribute tabanlı mini-ORM (SQLBase, DBBaseClass) Class/Dao/_SQLBase/*, DataLayout/DBBaseClass.vb 1 + 1 sınıf (aşağıda) VERIFIED
Runtime derlenen dinamik formlar (AIO.Dynamic.*) Class/Dao/Dynamic/* Yalnız DynamicWizard üzerinden VERIFIED
Typed DataSet (ERPGOLDV2DataSet) ERPPRO/ERPGOLDV2DataSet.Designer.vb 1 tablo adaptörü; tüketici yalnız SchedulerView tasarımcısı VERIFIED

Çok adımlı işlemlerde hiçbir yerde uygulama katmanı transaction'ı yoktur (bkz. §8).

2. Bağlantı dizesi — nerede tutuluyor (yapı, değer yok)

  1. Varsayılan sabit değer (kodda): InternalDb.ConnStr Shared Property'sinin başlangıç değeri bir yerel sunucu, sa kullanıcı adı ve düz metin parola içerir — VERIFIED ERPPRO/Class/Module/InternalDb.vb:11 (değer kopyalanmadı; AGENT_RULES §4). Uygulama açılışında üzerine yazılır ama derlenmiş binaride sabit olarak kalır.
  2. Çalışma zamanı kaynağı: Program.ConnectDatabaseInternalDb.ConnStr = Program.Config.ConnectionString ve aynı değer AifaDev.Common.ConnectionString'e — VERIFIED ERPPRO/Class/Module/Program.vb:78-87. Diğer kütüphanelere aktarım: TCMB.Currency.ConnStr (Program.vb:209), DijitalFatura.ERPFatura.ConnStr (FrmEFaturaBas.vb:8, FrmEFaturaListesi.vb:132), NetEntegretor.Common.ConnStr (Form/Siparis/FrmNetSiparis.vb:4).
  3. Configuration.Configuration sınıfı (ERPPRO/Configuration/ConfigurationForm.vb:97-329): SQLServer, SQLPort, IntegratedSecurity, SQLUserName, SQLPassword, PersistSecurityInfo, SQLDatabase, ApplicationName, WorkstationID özelliklerinden SqlConnectionStringBuilder ile dize üretir (:226-246). Kalıcı depo: uygulama dizinindeki Config.cfg INI dosyası, CONNECTION/ConnectionString anahtarı; içerik AES_Encrypt/AES_Decrypt ile şifreli (:297-328). Şifreleme: Rijndael ECB modu, MD5'ten türetilmiş anahtar, koda gömülü sabit parola (:301, :325, :331-365) — VERIFIED (değer kopyalanmadı).
  4. app.config: connectionStrings bölümünde üç giriş (ERPGOLDV2ConnectionString, localhost_Connection [XpoProvider], ERPGOLDV2ConnectionString1) — VERIFIED ERPPRO/app.config:9-13. Bunlar yalnız tasarımcı/typed dataset içindir; çalışma zamanı InternalDb.ConnStr'ı kullanır.
  5. Typed DataSet tasarımcısı: GetEfaturaListesiTableAdapter.InitConnection içinde bir sunucu IP'si ve sa kullanıcı adı sabit kodludurVERIFIED ERPPRO/ERPGOLDV2DataSet.Designer.vb:1730 (değer kopyalanmadı).
  6. SQL editörü: FrmSqlEditor kullanıcıya serbest bağlantı dizesi ve serbest SQL yürütme imkânı verir — VERIFIED ERPPRO/Form/Tanımlamalar/Sistem/FrmSqlEditor.vb:77-85.

3. InternalDb — bağlantı yaşam döngüsü

Kaynak: ERPPRO/Class/Module/InternalDb.vb (159 satır). VERIFIED.

Üye Satır Davranış
Shared Property ConnStr 11 Tüm örnekler için ortak bağlantı dizesi.
Property SqlConnection As New SqlConnection(ConnStr) 12 Her InternalDb örneği kendi SqlConnection nesnesini yaratır.
Property SqlCommand As New SqlCommand("", SqlConnection) 13 Tek komut nesnesi; parametreler db.SqlCommand.Parameters.AddWithValue ile dışarıdan eklenir.
Private Da As SqlDataAdapter, Private Ds As DataSet 14-15 DataSet/DataTable için; aynı Ds yeniden doldurulur, temizlenmez.
New(CommandText_) 18-20 Sorgu metni kurucuya verilir (Using db As New InternalDb("Select ...") kalıbı).
SqlConnectionState (set) 36-52 Open: kapalıysa açar; Close: açıksa kapatır. Hata → bir kez MessageBox "Sunucuya Ulaşılamıyor…" (UI çağrısı veri katmanında).
ExecuteNonQuery(SqlQuery) 53-65 Bağlantıyı açar, komutu çalıştırır; hata → MsgBox(ex.Message); Finally boş, bağlantı kapatılmaz.
ExecuteScalar(SqlQuery) 69-81 Aynı kalıp.
DataReader(SqlQuery) 85-96 ExecuteReaderCommandBehavior.CloseConnection yok; okuyucuyu kapatmak çağıranın sorumluluğu.
DataSet / DataTable 100-123 Da.Fill(Ds); Ds.Tables(0) döner. Aynı örnekte ikinci çağrı aynı Ds'ye ekler.
Dispose(disposing) 131-141 Gövde boşSqlConnection.Close/Dispose çağrılmaz.

Sonuçlar: - Using db As New InternalDb(...) (866 kullanım, grep "Using db As New InternalDb") bloğu bittiğinde bağlantı kapanmaz; açık bağlantı ancak çağıran açıkça db.SqlConnectionState = InternalDb.IDbConnectionState.Close derse (231 yer) veya GC SqlConnection'ı toplayınca havuza döner — VERIFIED (InternalDb.vb:131-141 + grep sayıları). Havuz tükenmesi riski INFERRED (üretimde gözlemlenmedi). - Satır içi New InternalDb("...").ExecuteScalar kalıbı (196 yer, grep "(New InternalDb(\|= New InternalDb(") hiçbir zaman dispose edilmez. - DataReader dönen kod çoğunlukla Dim Dr As Object ile geç bağlı çalışır (184 satır) ve Dr.Close/Dr.Dispose elle çağrılır (Database.vb:76-77 örneği). - Hatalar MsgBox ile kullanıcıya gösterilir ve yutulur; ExecuteNonQuery Nothing→0 döner, çağıran "başarısız" sanır ama sebep kaybolur (InternalDb.vb:60-61).

Diğer sarmalayıcı: Class/Module/SQLServers.vb yalnız ağdaki SQL Server örneklerini SqlDataSourceEnumerator ile ayrı bir Thread üzerinde listeler (yapılandırma ekranı için) — VERIFIED SQLServers.vb:4-25; boş Catch (:22-24).

4. DAO temel sınıfları

4.1 AIO.DBBase (ERPPRO/Class/Dao/Base.vb, 124 satır)

118 dosyada 119 sınıf Inherits DBBase (grep). Sağladıkları — VERIFIED: - ActiveSirketId/ActiveSubeIdProgram.SirketId/SubeId (:6-16); ActiveKasaId kendi kendini çağırır (Return ActiveKasaId, :18-22) → çağrılırsa StackOverflowException. Çağıran yok (grep 0) — latent. - Shared Event RecordInserted/RecordUpdated/RecordDeleted ve OnRecord… tetikleyicileri (:24-40). Tek dinleyici: Program.Shared Sub NewAfterSave (Program.vb:5-6, 8-19: Parcakarti kaydında ReceteDetay/PaketDetay miktarlarını iki ayrı UPDATE ile günceller, transaction yok). - Sanal Save/Insert/Update/Delete (:41-55) Nothing döner; her türetilmiş sınıf kendi SQL'ini yazar. - Shared Event Validate(Obj, ByRef Cancel) + IsValidate (:116-121): IsValid=True ile çağrılır, dinleyici yoksa True. Tek dinleyici Program.ValidateObject (Program.vb:20-30) parametreyi Validated anlamında kullanır (geçerliyse True) — yani bildirilen Cancel semantiğiyle ters; yalnız Parcakarti/Takimkarti/Paket/Modelkarti için doğrulama yapar, diğer her tip için True döner. Çağıranlar: Siparis.Parcakarti.vb:220, FrmModelKartıEkle.vb:100, FrmTakimEkle.vb:36, FrmPaketEkle.vb:39, FrmParcaKartiEkle.vb:93. (Mimari referans §9-10 "kırık" der; kodda: adlandırma ters, tek dinleyici ters kuralla uyumlu olduğu için 4 tipte çalışıyor, diğerlerinde doğrulama yokCONTRADICTS değil, ayrıntılandırma.)

4.2 DataLayout.DBBaseClass (ERPPRO/DataLayout/DBBaseClass.vb, 385 satır)

Attribute tabanlı (TableInfo, DataField(Identity, Binding)) reflection ORM: ReadFromTable (:16-33, Id string birleştirme), Insert (:42-87, alan listesi reflection'dan, parametreli, SCOPE_IDENTITY), Update (:88-143), Delete/DeleteAt (soft delete IsDeleted='True', :144-195). Her çağrıda New SqlConnection(InternalDb.ConnStr) açılıp Using ile kapanır (doğru yaşam döngüsü). Tek türetilmiş sınıf: DataLayout.RepositoryItem (DataLayout/Repository.vb:21-23, tablo AifaDev.RepositoryItems) — VERIFIED (grep Inherits DBBaseClass = 1).

4.3 AIO.SQL.SQLBase (ERPPRO/Class/Dao/_SQLBase/SQLBase.vb, 224 satır)

SQLTableName/SQLIdentity attribute'larıyla tek MERGE … WITH (SERIALIZABLE) … OUTPUT inserted.* upsert üretir (:38-64), parametreleri reflection'la doldurur (:65-88), Load(Id)/Load(Query)/Delete/Save sağlar. Tek türetilmiş sınıf: AIO.Cari.IFatura (Class/Dao/Islem/Cari.IDetay.vb:4-6, tablo Cari.IslemDetaylari) — VERIFIED. Kusur: Private Shared _Query (:31) → MERGE metni tüm türetilmiş sınıflar arasında paylaşılır; ikinci bir türetilmiş sınıf eklenirse ilk derlenen sorgu diğerine uygulanır (bugün tek sınıf olduğu için gizli).

4.4 Türetilmiş DAO sınıfları (kalıp)

ERPPRO/Class/Dao/*.vb (127 dosya) + Class/Ext/*.vb (66 dosya, Partial uzantılar). Her sınıf aynı iskeleti taşır — VERIFIED (scan, Save gövdeleri normalize edilip kümelendi): - New(RecordId)Select * From <tablo> Where Id='" & RecordId & "' + DataReader + alan alan If Not Dr.Item(x) Is DBNull.Value Then … (Cari.Islem.vb:19-…). - SaveIf Id = 0 Then Return Insert Else Return Update105 Save metodundan 91'i birebir bu gövde (52 + 39 yazım varyantı), 2'si HasCode kontrollü, 12 farklı. - InsertUsing db As New InternalDb("Insert Into … Values(@…) SELECT SCOPE_IDENTITY") + her alan için If X <> Nothing Then AddWithValue(@X, X) Else AddWithValue(@X, DBNull.Value) (110 Insert, 110 Update metodu Dao klasöründe). - Delete, Delete(RecordId), Shared DeleteAt(Id) üçlüsü: 87 dosyada (soft delete). - SetPropertyValue/GetPropertyValue reflection çifti: 121 dosyada kopya (grep "Sub SetPropertyValue(ByVal PropertyName"). Ayrıntılı çiftleme tabloları: hotspots §6.

5. Dinamik formlar — AIO.Dynamic.* (runtime derleme)

ERPPRO/Class/Dao/Dynamic/ (1309 satır sınıf + formlar). VERIFIED: - Compiler(Of ReturnType) (Class/Compiler.vb:4-74): VBCodeProvider.CompileAssemblyFromSource ile çalışma zamanında VB kaynak derler (:59). - DynamicForm.Execute (Class/Base/DynamicForm.vb:300-318): AifaDev.DynamicForms.Code kolonundaki kaynak kodu derleyip AIO.Dynamic.Data türetilmiş nesne üretir; hata → MsgBox. - AIO.Dynamic.Data (Class/Base/Data.vb): reflection'la Insert/Update (parametreli, :192-240), ToList (:134); 8 boş Catch. - AIO.Dynamic.Table (Class/Base/Table.vb:92-160): sys.columns sorgusu ve CREATE/ALTER TABLE betiği üretimi — tablo/kolon adları string birleştirme (:145,152,160), Exec ('…'). - Giriş noktası: FrmMainForm.onMenuSelect"FORMWIZARD"AIO.Dynamic.Design.DynamicWizard (Form/Genel/FrmMainForm.vb:98-101). Ayrıca Utils.cVBEvalProvider.Eval (Class/Module/Utils.vb:198-…) ikinci bir runtime derleyicidir; çağıranı yok (grep) — ölü kod. - Derlenen kod uygulama ile aynı süreçte, aynı yetkiyle çalışır; kaynak veritabanı tablosundan gelir → veritabanına yazma yetkisi olan herkes uygulama içinde rastgele .NET kodu çalıştırabilir (INFERRED, güvenlik etkisi technical-debt TD-19).

6. Typed DataSet ve DevExpress XPO

  • ERPGOLDV2DataSet (ERPPRO/ERPGOLDV2DataSet.xsd + .Designer.vb, 2159 satır): tek tablo adaptörü GetEfaturaListesiTableAdapter (:1581, sorgu SELECT Id, Tarih, Unvan, … :1739, @StartDate/@EndDate parametreli). TableAdapterManager.UpdateAll (:1917-1992) depodaki tek SqlTransaction kullanımıdır (BeginTransaction :1938, Commit :1970, Rollback :1992) — ancak UpdateAll hiçbir yerden çağrılmaz; dataset'i kullanan tek yer Organizasyon/Task/SchedulerView.Designer.vb:108,781 (BindingSource olarak). VERIFIED: scan transaction=True yalnız bu sınıfta.
  • XPO: ERPPRO/ERPPRO.vbproj:180 DevExpress.Xpo.v14.1 referansı ve app.config:11 XpoProvider=MSSqlServer bağlantısı var; ancak grep -i "XpoDefault|XPCollection|UnitOfWork|XPObject" ERPPRO kaynaklarında 0 sonuç → XPO kullanılmıyor, artık referans — VERIFIED.

7. DSX önbelleği (ERPPRO/Class/Module/Database.vb, 695 satır)

DSX.TABLEShared listeler (:3-26): ListOfController, ListOfCariIslemKodlari, ListOfMaden, ListOfHesap, ListOfNakit, ListOfProje, ListOfBirim, ListOfKasalar, ListOfSubeler, ListOfHizmetler, ListOfVadeliKod, ListOfAnlikFiyat, ListOfParite, ListOfTakozTipi, ListOfVariant, ListOfSSKGrup, ListOfCikisKodu, ListOfMeslekKodu, ListOfListLayout, ListOfCariIslemTipi, UrunListesi. VERIFIED.

DSX.DAO.RetriveOfAllData (:556-607) tek InternalDb ile sırayla yükler:

Sıra Metot Kaynak sorgu (tablo) Satır
0 ListOfCariIslemTipi ← AIO.Sistem.CariIslemTipi.TOList Sistem.CariIslemTipi 558-559
1 RetriveOfController Agent.Controller Where IsDeleted='False' 54-79
2 RetriveOfBirim Sistem.Birimler … SirketId=… 110-135
3 RetriveOfMaden Stok.Maden … SirketId=… 178-220
4 RetriveOfNakit Stok.Nakit … SirketId=… 221-244
5 RetriveOfHesap Cari.HesapListesi Order by HesapAdi (view; Password kolonu dahil) 245-293
6 RetriveOfHizmet Stok.Hizmet 294-314
7 RetriveOfVadeliKod Sistem.Vadeli 315-338
8 RetriveOfSube Sistem.Subeler 360-400
9 RetriveOfKasa Sistem.Kasalar … SirketId=… 339-359
10 RetriveOfFiyatTablosu Sistem.FiyatTablosu … SubeId=… 154-177
11 RetriveOfTakozTipi Sistem.TakozTipleri 426-446
12 RetriveOfParite Sistem.Pariteler 136-153
13 RetriveOfCariIslemKodlari Sistem.CariIslemKodu ∪ CariIslemTuru ∪ CariIslemTipi 401-415
14 RetriveOfVariant Stok.Varyant 447-469
15 RetriveOfPersonelCikisNedeni Personel.CikisNedenleri 512-533
16 RetriveOfMeslekKodu Personel.MeslekKodlari 491-511
17 RetriveOfSSKGrup Personel.SSKGrup 470-490
18 RetriveOfListLayout Sistem.ListLayout 534-553
19 RetriveOfProjeler Genel.Projeler 80-109
20 RetriveOfRibbonPage, RetriveOfThreadGrid, RetriveOfDbCombo, UrunListesi.LoadStok Ribbon tasarımı, popup grid, combo kaynakları, stok listesi 598-604

Ne zaman yenilenir — VERIFIED: - Girişte: Program.vb:217 (GlobalSettings.Read sonrası, KurListesi.Start öncesi). - Elle: Ana menü "RETRIVEALLDATA" (Form/Genel/FrmMainForm.vb:329-332), "AKTARIM" (FrmHesapAktar sonrası, :372-376), FrmDevirGiris.vb:83. - Kısmi: RetriveOfHesap yalnız FrmHesapTemizle.vb:30; RetriveOfMaden yalnız FrmZiynetIscilikleri.vb:69. ListOfHesap.RemoveAt Cari.Hesap.vb:824 (silmede). ListOfHesap.Add yalnız Database.vb'de → yeni hesap kaydından sonra önbelleğe ekleyen kod bulunamadı; INFERRED yeni hesap, RETRIVEALLDATA veya yeniden giriş yapılana kadar iHesapControl combolarında görünmez [GAP: FrmCariHesapEkle çağıranlarında yenileme aranmadı — iScreen.vb:320 salt görüntüleme]. - Olaylar: her RetriveOfX içinde RaiseEvent RetriveX var ve RetriveOfAllData aynı olayı tekrar yükseltir (:78 + :562, :134 + :564, :219 + :566 …) → dinleyiciler iki kez çalışır — VERIFIED. - Bütün RetriveOfX sorguları Program.SirketId/SubeId değerini string birleştirmeyle gömer (:113,157,181,224,297,342,429); değer tam sayı olduğundan enjeksiyon riski düşük. - RetriveOfProjeler/TakozTipi/Variant/SSKGrup/MeslekKodu/CikisNedeni/ListLayout boş Catch içinde (:105,443,466,487,508,530,550).

DSX.FN (:611-689): önbellek üstünde yardımcılar (GetAnaBirim, GetIdToBirim, GetCariIslemKodu, GetKarsiIslemTuru, GetHesapAdi) + iki skaler SQL fonksiyonu çağrısı (FN.GET_ERP_ISTATISTIK_KODU :613, Stok.GetTakozIsciligi :625, ikisi de string birleştirme).

Döviz kuru önbelleği ayrıdır: AIO.Kur.KurListesi (Class/Dao/Sistem.KurListesi.vb:60-127) System.Threading.Timer ile 15 sn (GlobalSettings.KurRefreshInterval) aralıkla Sistem.GetKurListesi(GETDATE) okur (:74,100); Program.GetKur(Birim) her zaman Alis döner, birim yoksa 1 döner (Program.vb:260-268). Liste UI iş parçacığı dışından değiştirilir, kilit yok — INFERRED yarış.

8. Sorgu kalıpları ve sayılar

Kaynak: scan sql girdileri (1468 ifade) + grep. VERIFIED.

Kalıp Sayı Not
Toplam SQL ifadesi (scan) 1468 793 select, 578 update, 161 insert, 27 delete, 9 exec, 1 truncate
Dinamik (string birleştirme içeren) 1010 / 1468 (%69) 311 sınıfta
Statik metin 458 çoğu Insert/Update … @param DAO kalıbı
İfadesi olan metotlardan Parameters.AddWithValue/Add kullananlar 290 / 1296 metot (%22) 142 dosyada 4559 AddWithValue satırı — neredeyse tamamı DAO Insert/Update
Dinamik ifadelerden parametre de kullanan metot içindekiler 32 / 1010 karışık kullanım (ör. Cari.Islem.Insert parametreli INSERT + "Cari.RsAktar " & RecId)
Tırnak içine gömülü '" & X & "' içeren satır 568 çoğu Id/SirketId gibi tam sayı; string olanlar aşağıda
Elle SQL yazan sınıf 364 sql_count > 0
InternalDb kullanan sınıf 293
Saklı yordam çağrısı ~28 çağrı, 16 dosya EXECli 8 (Siparis.CreateUretimSiparisi, sys.sp_configure, NET.StokYaz, Cari.IslemDetayYaz, SPSort, bilanco.SaveAll×2, dinamik Exec(…)) + EXECsiz 20 (Cari.FaturaYaz×8, Cari.RsAktar, Takoz.RaporYaz, Organizasyon.GorevDagit, Sistem.GetFaturaNo×3, Sistem.GetLastFaturaNo, Cari.MadenSiparisTeslimET, Sistem.FromERP, …). CommandType.StoredProcedure 0; tümü metin komutu, parametreler birleştirilmiş.
Tablo değerli fonksiyon (From Sema.Fn(...)) 154 ifade, 95 sınıf veritabanında 281 inline TVF (kb_src/db/modules_all.json); liste/rapor katmanı SQL tarafında
Skaler fonksiyon (Select Sema.Fn(...)) 7 ifade, 4 sınıf FN.GetCariLimit, Cari.GetNewSiraNo, Cari.GetFisPesinKontrol, FN.ProvizyonIzin, Stok.GetTakozIsciligi, Siparis.GetLastPrice
Grid/Liste kontrolüne .SQL = "…" ataması 31 SimpleList, DataListGrid sorguyu kontrol içinde çalıştırır

8.1 String birleştirme örnekleri (SQL enjeksiyonu açısından string parametreler) — 10 adet, VERIFIED

# Dosya:satır Gömülen değer Kaynak
1 ERPPRO/Form/Doviz/FrmDovFaturaEkranı.vb:239-243 RefNo InputBox ile kullanıcıdanUpdate Dov.Fatura Set IsDeleted='True' where RefNo='…'
2 ERPPRO/Controls/Islem Ekranı/Ekran/Fatura Helper/FindTaxInfo.vb:25 (ve FrmFaturaBas.vb:140) SearchText Metin kutusu → Select * from Cari.FindTaxInfo('…')
3 ERPPRO/Controls/Editor/CmbListLayout.vb:110,116 Ad, ListeTipi Kullanıcının girdiği düzen adı → Insert Into Sistem.Layout(...) Values('…')
4 ERPPRO/Class/Dao/Personel.Personel.vb:29 TcNo Where TcKimlikNo ='…'
5 ERPPRO/Class/Dao/Stok.Model.vb:248,300 (aynısı Stok.Pirlanta.vb:444,469, Siparis.ModelEx.vb:52, Siparis.ParcaEx.vb:56) Kodu, Value Stok kodu (kullanıcı girişi) → Where Kodu='…'
6 ERPPRO/Class/Dao/Sistem.Clients.vb:232 ComputerName My.Computer.NameDelete From Sistem.Clients Where ClientName='…' (hard delete)
7 ERPPRO/Class/Module/Extention.vb:99 TableName, FieldName, Value Genel FindRecord — tablo/alan adı da birleştiriliyor
8 ERPPRO/Class/Ext/Dov.HaraketFoyu.vb:38 Kodu, StokCinsi Where StokKodu='…' And StokCinsi='…'
9 ERPPRO/Class/Dao/Siparis.Recete.vb:102, Class/Ext/Siparis.ReceteEx.vb:51 Kodu Reçete kodu
10 ERPPRO/Controls/Islem Ekranı/Base Control/iScreen.vb:975 Filter, OrderString, HesapType Grid filtre/sıralama metni doğrudan sorguya ekleniyor

Ek: Class/Module/Devir.vb:39-40,141 (veritabanı adı, yedek yolu, DevirNameBACKUP/RESTORE ve Insert Into ERPGLOBAL.Dbo.Company), Class/RFID/PrintBase.vb:82 (TableName), DataLayout/DBBaseClass.vb:17, _SQLBase/SQLBase.vb:138-163 (Id değerleri).

Enjeksiyon açısından değerlendirme (INFERRED): uygulama masaüstü/iç ağ ve kullanıcılar kimlikli; ancak sa ile bağlanıldığı için (§2) tek bir ' içeren serbest metin girdisi en azından sorguyu bozar, bilinçli girdi ise veri değiştirebilir. Tam-sayı birleştirmeleri (Id, SirketId) Val/Integer tipinden geldiği için pratik risk taşımaz.

9. Transaction / atomiklik

Arama sonuçları — VERIFIED: - SqlTransaction, BeginTransaction: yalnız ERPGOLDV2DataSet.Designer.vb:1588,1644-1665,1938-1992 (üretilmiş kod, çağıranı yok). - TransactionScope: 0. - BEGIN TRAN / Begin Tran uygulama SQL metinlerinde: 0 (yalnız DB tarafında trigger/SP içinde ROLLBACK). - scan transaction bayrağı yalnız TableAdapterManager.UpdateAll.

Sonuç: uygulama katmanındaki her çok adımlı işlem, ayrı SqlConnection/SqlCommand'lerle ve ara adım başarısız olursa geri alınmadan çalışır. Beş somut örnek:

# İşlem Adımlar Dosya:satır Başarısızlık etkisi
1 Fatura + detay + FaturaYaz Insert Into Cari.Faturalar … SCOPE_IDENTITYCari.FaturaYaz <Id> (SP) ; ayrıca her detay satırı FrmFaturaIslemEkle.FormSave ile ayrı Detay.Save → her biri yine Cari.FaturaYaz çağırır. Silme: UPDATE FaturalarUPDATE FaturaDetayFaturaYaz ERPPRO/Class/Dao/Cari.Fatura.vb:229-230 (Insert), :313-317 (DeleteAt), Cari.FaturaDetay.vb:167,209,229, Form/Fatura/FrmFaturaIslemEkle.vb:129-146 Başlık yazılır, FaturaYaz hata verirse (Catch boş, :231) türetilmiş kayıtlar eksik kalır; başlık ile detay farklı zamanlarda, her detay ayrı bağlantıda.
2 İşlem + RsAktar Insert Into Cari.Islemler … SCOPE_IDENTITYRSAktar=True ve şube RsAktar ise db.ExecuteNonQuery("Cari.RsAktar " & RecId) (aynı InternalDb, ayrı komut) ; TAKOZ girişinde Update sonrası ek komut ERPPRO/Class/Dao/Islem/Cari.Islem.vb:695-696 (Insert), :989 (Update) RsAktar başarısız olursa Catch boş (:697), işlem kayıtlı kalır, resmî hesaba aktarım yapılmamış olur; Save True döner.
4 Dönem devri (Devir.DevirYap) sp_configureBACKUP DATABASERESTORE DATABASE (arşiv) → sayım doğrulama → Update Subeler (arşiv) → TRUNCATE Cari.Islemler, Cari.BakiyeIslem, Cari.History, Stok.StokHaraketleri, Cari.IslemDetaylari + geçici tablolardan yeniden INSERTERPGLOBAL DB oluşturma → Insert Into ERPGLOBAL.Dbo.Company ERPPRO/Class/Module/Devir.vb:37-155 (DevirQuery :41-87, yürütme :90-147) DevirQuery tek ExecuteNonQuery metnidir ama BEGIN TRAN içermez; TRUNCATE'ten sonra INSERT hata verirse hareket tabloları boş kalır; öncesinde alınan yedek tek güvence.
5 Reçete / sipariş kopyalama Recete.Save (yeni başlık) → ReceteDetay.ToList(eskiId) döngüsünde her satır itm.Save ERPPRO/Class/Ext/Siparis.ReceteEx.vb:49-74 (:61-68); aynı kalıp Siparis.SiparisEx.vb:39-60, Siparis.UretimSiparisiEx.vb:57 Döngü ortasında hata → yarım kopya (başlık + kısmi detay), dönüş değeri yine yeni Id.

Ek örnekler: Program.AfterSave iki ayrı UPDATE (Program.vb:11-14); FrmCariHesapEkle.FormSaveK.Save + DataLayoutControl1.Save + karşı hesap UPDATE (Form/Tanımlamalar/Cari/FrmCariHesapEkle.vb:223-229); FrmHesapAktar üç Count sorgusu sonra Sistem.FromERP SP (Form/Tanımlamalar/Sistem/FrmHesapAktar.vb:8-22).

Veritabanı tarafındaki telafi: Cari.Islemler tetikleyicileri (AcceptTransaction vb.) INSERT/UPDATE'i kendi içinde ROLLBACK edebilir; ancak demo kopyasında dördü de DISABLED (kb_src/projdocs/erpgoldv2-kod-katmani-bulgulari.md §2, AS-DOCUMENTED) ve ROLLBACK sonrası RETURN bazı dallarda yok (AcceptTransaction satır 122, 184, 231, 245 — kb_src/db/modules_all.json, VERIFIED). Ayrıntı: technical-debt TD-03/TD-04.

10. Yeniden yazım için çıkarımlar (öneri, uygulanmadı)

  • Tek veri erişim yolu: InternalDb'nin yerini Using ile açılıp kapanan, IDisposable'ı doğru uygulayan, her zaman parametreli bir komut fabrikası almalı; MsgBox veri katmanından çıkmalı.
  • Save zinciri (Islem + RsAktar, Fatura + Detay + FaturaYaz, SendTo, Copy) tek SqlTransaction/TransactionScope içine alınmalı; SP'ler CommandType.StoredProcedure ve SqlParameter ile çağrılmalı.
  • DSX.TABLE önbelleği kayıt sonrası olaylarla (RecordInserted/Updated/Deleted) güncellenmeli; olaylar tek yerden yükseltilmeli.
  • Bağlantı dizesi ve şifreleme parolası koddan çıkarılmalı (DPAPI/Windows kimlik doğrulaması), sa yerine en az yetkili hesap.
  • SQLBase/DBBaseClass/Dynamic üçlüsünden biri seçilip diğerleri kaldırılmalı (bugün toplam 2 üretim sınıfı kullanıyor).