Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
SQL Server zamana bağlı tabloları , veriler güncelleştirildikten veya silindikten sonra bile bir tabloda depolanan tüm verileri otomatik olarak izler. Bu, ana tabloda her değişiklik yapıldığında zaman damgasına sahip geçmiş verilerin depolandığı paralel bir "geçmiş tablosu" oluşturularak elde edilir. Bu, geçmiş verilerin denetleme gibi amaçlarla sorgulanabilmesini veya yanlışlıkla yapılan değişiklik ya da silme sonrası kurtarma gibi amaçlarla geri yüklenebilmesini sağlar.
EF Core şu desteği destekler:
- EF Core Migrations kullanılarak zamana bağlı tablolar oluşturma
- Geçişleri kullanarak mevcut tabloların zamansal tablolara dönüştürülmesi
- Geçmiş verileri sorgulama
- Geçmişteki bir noktadan verileri geri yükleme
Zamansal tablo yapılandırma
Model oluşturucu, bir tabloyu zamansal olarak yapılandırmak için kullanılabilir. Örneğin:
modelBuilder
.Entity<Employee>()
.ToTable("Employees", b => b.IsTemporal());
Tavsiye
Burada gösterilen kod TemporalTablesSample.cs gelir.
Veritabanını oluşturmak için EF Core kullanılırken, yeni tablo zaman damgaları ve geçmiş tablosu için SQL Server varsayılanlarıyla bir zamansal tablo olarak yapılandırılır. Örneğin, bir Employee
varlık türü düşünün:
public class Employee
{
public Guid EmployeeId { get; set; }
public string Name { get; set; }
public string Position { get; set; }
public string Department { get; set; }
public string Address { get; set; }
public decimal AnnualSalary { get; set; }
}
Oluşturulan zamana bağlı tablo şöyle görünür:
DECLARE @historyTableSchema sysname = SCHEMA_NAME()
EXEC(N'CREATE TABLE [Employees] (
[EmployeeId] uniqueidentifier NOT NULL,
[Name] nvarchar(100) NULL,
[Position] nvarchar(100) NULL,
[Department] nvarchar(100) NULL,
[Address] nvarchar(1024) NULL,
[AnnualSalary] decimal(10,2) NOT NULL,
[PeriodEnd] datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
[PeriodStart] datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
CONSTRAINT [PK_Employees] PRIMARY KEY ([EmployeeId]),
PERIOD FOR SYSTEM_TIME([PeriodStart], [PeriodEnd])
) WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [' + @historyTableSchema + N'].[EmployeeHistory]))');
SQL Server'ın datetime2
ve PeriodEnd
adında iki gizli PeriodStart
sütun oluşturduğuna dikkat edin. Bu "nokta sütunları", satırdaki verilerin bulunduğu zaman aralığını temsil eder. Bu sütunlar EF Core modelindeki gölge özelliklere eşlenir ve daha sonra gösterildiği gibi sorgularda kullanılmasına olanak sağlar.
Önemli
Bu sütunlardaki saatler her zaman SQL Server tarafından oluşturulan UTC saatidir. UTC saatleri, aşağıda gösterilen sorgular gibi zamana bağlı tabloları içeren tüm işlemler için kullanılır.
Ayrıca adlı EmployeeHistory
ilişkili bir geçmiş tablosunun otomatik olarak oluşturulduğuna da dikkat edin. Dönem sütunlarının ve geçmiş tablosunun adları, model oluşturucusunun ek yapılandırmasıyla değiştirilebilir. Örneğin:
modelBuilder
.Entity<Employee>()
.ToTable(
"Employees",
b => b.IsTemporal(
b =>
{
b.HasPeriodStart("ValidFrom");
b.HasPeriodEnd("ValidTo");
b.UseHistoryTable("EmployeeHistoricalData");
}));
Bu, SQL Server tarafından oluşturulan tabloya yansıtılır:
DECLARE @historyTableSchema sysname = SCHEMA_NAME()
EXEC(N'CREATE TABLE [Employees] (
[EmployeeId] uniqueidentifier NOT NULL,
[Name] nvarchar(100) NULL,
[Position] nvarchar(100) NULL,
[Department] nvarchar(100) NULL,
[Address] nvarchar(1024) NULL,
[AnnualSalary] decimal(10,2) NOT NULL,
[ValidFrom] datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
[ValidTo] datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
CONSTRAINT [PK_Employees] PRIMARY KEY ([EmployeeId]),
PERIOD FOR SYSTEM_TIME([ValidFrom], [ValidTo])
) WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [' + @historyTableSchema + N'].[EmployeeHistoricalData]))');
Zamana bağlı tabloları kullanma
Çoğu zaman, zamansal tablolar diğer tablolarda olduğu gibi kullanılır. Başka bir ifadeyle, dönem sütunları ve geçmiş verileri SQL Server tarafından saydam bir şekilde işlenir ve böylece uygulama bunları yoksayabilir. Örneğin, yeni varlıklar veritabanına normal şekilde kaydedilebilir:
context.AddRange(
new Employee
{
Name = "Pinky Pie",
Address = "Sugarcube Corner, Ponyville, Equestria",
Department = "DevDiv",
Position = "Party Organizer",
AnnualSalary = 100.0m
},
new Employee
{
Name = "Rainbow Dash",
Address = "Cloudominium, Ponyville, Equestria",
Department = "DevDiv",
Position = "Ponyville weather patrol",
AnnualSalary = 900.0m
},
new Employee
{
Name = "Fluttershy",
Address = "Everfree Forest, Equestria",
Department = "DevDiv",
Position = "Animal caretaker",
AnnualSalary = 30.0m
});
await context.SaveChangesAsync();
Bu veriler daha sonra normal şekilde sorgulanabilir, güncelleştirilebilir ve silinebilir. Örneğin:
var employee = await context.Employees.SingleAsync(e => e.Name == "Rainbow Dash");
context.Remove(employee);
await context.SaveChangesAsync();
Ayrıca, normal bir izleme sorgusundan sonra, geçerli verilerin dönem sütunlarından değerlere izlenen varlıklardan erişilebilir. Örneğin:
var employees = await context.Employees.ToListAsync();
foreach (var employee in employees)
{
var employeeEntry = context.Entry(employee);
var validFrom = employeeEntry.Property<DateTime>("ValidFrom").CurrentValue;
var validTo = employeeEntry.Property<DateTime>("ValidTo").CurrentValue;
Console.WriteLine($" Employee {employee.Name} valid from {validFrom} to {validTo}");
}
Çıktı olarak bu gösterilir:
Starting data:
Employee Pinky Pie valid from 8/26/2021 4:38:58 PM to 12/31/9999 11:59:59 PM
Employee Rainbow Dash valid from 8/26/2021 4:38:58 PM to 12/31/9999 11:59:59 PM
Employee Fluttershy valid from 8/26/2021 4:38:58 PM to 12/31/9999 11:59:59 PM
Sütunun ValidTo
(varsayılan olarak PeriodEnd
olarak adlandırılır) datetime2
maksimum değeri içerdiğine dikkat edin. Tablodaki geçerli satırlar için durum her zaman böyledir.
ValidFrom
sütunları (varsayılan olarak PeriodStart
olarak adlandırılan), satırın eklendiği UTC saatini içerir.
Geçmiş verileri sorgulama
EF Core, çeşitli özel sorgu işleçleri aracılığıyla geçmiş verileri içeren sorguları destekler:
-
TemporalAsOf
: Belirtilen UTC saatinde etkin olan (geçerli) satırları döndürür. Bu, belirli bir birincil anahtar için geçerli tablo veya geçmiş tablosundan tek bir satırdır. -
TemporalAll
: Geçmiş verilerdeki tüm satırları döndürür. Bu genellikle belirli bir birincil anahtar için geçmiş tablosundan ve/veya geçerli tablodan birçok satırdır. -
TemporalFromTo
: verilen iki UTC saat arasında etkin olan tüm satırları döndürür. Bu, belirli bir birincil anahtar için geçmiş tablosundaki ve/veya geçerli tablodaki birçok satır olabilir. -
TemporalBetween
:TemporalFromTo
ile aynıdır, ancak üst sınırda etkin hale gelen satırların dahil edilmesi söz konusudur. -
TemporalContainedIn
: Etkin olmaya başlayan ve verilen iki UTC saat arasında etkin olmaya son veren tüm satırları döndürür. Bu, belirli bir birincil anahtar için geçmiş tablosundaki ve/veya geçerli tablodaki birçok satır olabilir.
Uyarı
Bu işleçlerin her biri için tam olarak hangi satırların dahil olduğu hakkında daha fazla bilgi için SQL Server zamana bağlı tablolar belgelerine bakın.
Örneğin, verilerimizde bazı güncelleştirmeler ve silmeler yaptıktan sonra geçmiş verileri görmek için kullanarak TemporalAll
bir sorgu çalıştırabiliriz:
var history = await context
.Employees
.TemporalAll()
.Where(e => e.Name == "Rainbow Dash")
.OrderBy(e => EF.Property<DateTime>(e, "ValidFrom"))
.Select(
e => new
{
Employee = e,
ValidFrom = EF.Property<DateTime>(e, "ValidFrom"),
ValidTo = EF.Property<DateTime>(e, "ValidTo")
})
.ToListAsync();
foreach (var pointInTime in history)
{
Console.WriteLine(
$" Employee {pointInTime.Employee.Name} was '{pointInTime.Employee.Position}' from {pointInTime.ValidFrom} to {pointInTime.ValidTo}");
}
EF.Property yöntemi'nin, dönem sütunlarından değerlere erişmek için nasıl kullanılabildiğine dikkat edin. Bu, yan tümcesinde OrderBy
verileri sıralamak için kullanılır ve sonra bu değerleri döndürülen verilere eklemek için bir projeksiyonda kullanılır.
Bu sorgu aşağıdaki verileri geri getirir:
Historical data for Rainbow Dash:
Employee Rainbow Dash was 'Ponyville weather patrol' from 8/26/2021 4:38:58 PM to 8/26/2021 4:40:29 PM
Employee Rainbow Dash was 'Wonderbolt Trainee' from 8/26/2021 4:40:29 PM to 8/26/2021 4:41:59 PM
Employee Rainbow Dash was 'Wonderbolt Reservist' from 8/26/2021 4:41:59 PM to 8/26/2021 4:43:29 PM
Employee Rainbow Dash was 'Wonderbolt' from 8/26/2021 4:43:29 PM to 8/26/2021 4:44:59 PM
Dikkat edin: Döndürülen son satır 26.08.2021 16:44:59'da aktif olmayı durdurdu. Bunun nedeni Rainbow Dash satırının o sırada ana tablodan silinmesidir. Bu verilerin nasıl geri yüklenebileceğini daha sonra göreceğiz.
Benzer sorgular TemporalFromTo
, TemporalBetween
veya TemporalContainedIn
kullanılarak yazılabilir. Örneğin:
var history = await context
.Employees
.TemporalBetween(timeStamp2, timeStamp3)
.Where(e => e.Name == "Rainbow Dash")
.OrderBy(e => EF.Property<DateTime>(e, "ValidFrom"))
.Select(
e => new
{
Employee = e,
ValidFrom = EF.Property<DateTime>(e, "ValidFrom"),
ValidTo = EF.Property<DateTime>(e, "ValidTo")
})
.ToListAsync();
Bu sorgu aşağıdaki satırları döndürür:
Historical data for Rainbow Dash between 8/26/2021 4:41:14 PM and 8/26/2021 4:42:44 PM:
Employee Rainbow Dash was 'Wonderbolt Trainee' from 8/26/2021 4:40:29 PM to 8/26/2021 4:41:59 PM
Employee Rainbow Dash was 'Wonderbolt Reservist' from 8/26/2021 4:41:59 PM to 8/26/2021 4:43:29 PM
Geçmiş verileri geri yükleme
Yukarıda belirtildiği gibi, Rainbow Dash tablodan Employees
silindi. Bu açıkça bir hataydı, bu nedenle belirli bir noktaya geri dönelim ve o zamandan itibaren eksik satırı geri yükleyelim.
var employee = await context
.Employees
.TemporalAsOf(timeStamp2)
.SingleAsync(e => e.Name == "Rainbow Dash");
context.Add(employee);
await context.SaveChangesAsync();
Bu sorgu, belirtilen UTC saatinde olduğu gibi Rainbow Dash için tek bir satır döndürür. Zamana bağlı işleçleri kullanan tüm sorgular varsayılan olarak izlenmez, bu nedenle burada döndürülen varlık izlenmez. Bu, şu anda ana tabloda mevcut olmadığından mantıklıdır. Varlığı ana tabloya yeniden eklemek için bunu olarak Added
işaretlememiz ve çağrısı SaveChanges
yapmamız yeterlidir.
Rainbow Dash satırını yeniden ekledikten sonra geçmiş verileri sorgulamak satırın verilen UTC saatinde olduğu gibi geri yüklendiğini gösterir:
Historical data for Rainbow Dash:
Employee Rainbow Dash was 'Ponyville weather patrol' from 8/26/2021 4:38:58 PM to 8/26/2021 4:40:29 PM
Employee Rainbow Dash was 'Wonderbolt Trainee' from 8/26/2021 4:40:29 PM to 8/26/2021 4:41:59 PM
Employee Rainbow Dash was 'Wonderbolt Reservist' from 8/26/2021 4:41:59 PM to 8/26/2021 4:43:29 PM
Employee Rainbow Dash was 'Wonderbolt' from 8/26/2021 4:43:29 PM to 8/26/2021 4:44:59 PM
Employee Rainbow Dash was 'Wonderbolt Trainee' from 8/26/2021 4:44:59 PM to 12/31/9999 11:59:59 PM