https://github.com/sulmar/ef-tutorial
Przewodnik po Entity Framework 6.x
https://github.com/sulmar/ef-tutorial
csharp entity-framework
Last synced: 14 days ago
JSON representation
Przewodnik po Entity Framework 6.x
- Host: GitHub
- URL: https://github.com/sulmar/ef-tutorial
- Owner: sulmar
- Created: 2017-01-14T20:40:29.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2021-04-20T06:44:18.000Z (about 5 years ago)
- Last Synced: 2025-02-21T20:13:31.331Z (over 1 year ago)
- Topics: csharp, entity-framework
- Homepage:
- Size: 52.7 KB
- Stars: 3
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Instalacja biblioteki
~~~ bash
PM> Install-Package EntityFramework
~~~
# Utworzenie kontekstu
~~~ csharp
public class MyContext : DbContext
{
public MyContext(DbConnection connection, bool contextOwnsConnection)
:base(connection, contextOwnsConnection)
{ }
public DbSet Customers { get; set; }
public DbSet Orders { get; set; }
}
~~~
# Połączenie do bazy danych
~~~ xaml
~~~
## Algorytm wyszukiwania bazy danych
1. Jeśli w konstruktorze DbContext podano nazwę połączenia to szuka jej w pliku konfiguracynym app.config w sekcji _connectionStrings_
2. Jeśli użyto domyślnego konstruktora DbContext to szuka pliku konfiguracynym app.config nazwy klasy DbContext w sekcji _connectionStrings_
3. Szuka instancji SQL Express
4. Szuka bazy danych LocalDb o adresie (localdb)\mssqllocaldb
## Wyświetlenie parametrów połączenia
~~~ csharp
Console.WriteLine(context.Database.Connection.ConnectionString);
~~~
- Azure Data Studio
https://docs.microsoft.com/en-us/sql/azure-data-studio/download?view=sql-server-2017
# DbContext
Klasa DbContext jest główną częścią Entity Framework. Instacja DbContext reprezentuje sesję z bazą danych.
## Zadania DbContext:
1. **Querying** - Konwertuje Linq-To-Entities do zapytań SQL i wysyła je do bazy danych
2. **Change Tracking** - śledzenie zmian
3. **Persisting Data** - Zapisywanie zmian encji w bazie danych
4. **Caching** - pamięć podręczna pierwszego poziomu
5. **Manage Relationship** - zarządzanie relacjami
6. **Transactions** - zarządzanie transakcjami
7. **Object Materialization** - konwersja surowych danych do obiektów encji
Models.cs
~~~ csharp
public class Order
{
public int OrderId { get; set; }
public string OrderNumber { get; set; }
public DateTime OrderDate { get; set; }
public DateTime DeliveryDate? { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsDeleted { get; set; }
}
~~~
MyContext.cs
~~~ csharp
public class MyContext : DbContext
{
public DbSet Customers { get; set; }
public DbSet Orders { get; set; }
public CustomersContext()
: base("MyDbConnection")
{
}
}
~~~
## Właściwości DbContext
| Metoda | Użycie |
|---|---|
| ChangeTracker | Dostarcza informacje i operacje do śledzenie obiektów |
| Database | Dostarcza informacje o bazie danych i umożliwia operacje na bazie danych |
| Configuration | Konfiguracja opcji |
## Fabryka
Jeśli korzystamy z migracji, a konstuktor naszej klasy DbContext posiada parametr(y) nalezy utworzyć fabrykę:
~~~ csharp
public class MyContextFactory : IDbContextFactory
{
public MyContext Create()
{
return new MyContext(new TransportDbInitializer());
}
}
~~~
# Konwencje
## Odkrywanie typów (Type Discovery)
EF wyszukuje typy, która wskazane są poprzez właściwość DbSet i tworzy odpowiadające im tabele.
Uwględnia również referencyjne typy właściwości, które nie są wskazane przez DbSet oraz typy, które dziedziczą po klasie bazowej wskazanej przez DbSet.
## Konwencja nazywania tabel (Table Name Convention)
Code First tworzy tabele o nazwach w liczbie mnogiej od nazwy encji.
## Nazwy kolum
EF wyszukuje wszystkie publiczne właściwości, które posiadają get i set i tworzy kolumny o takich samych nazwach.
## Typy danych i rozmiar kolumn
| .NET | SQL |
|---|---|
| string | nvarchar(max) |
| decimal | decimal(18, 2) |
| double | float |
| int | int |
| bool | bit |
| DateTime | datetime |
| byte[] | varbinary(max) |
## Klucz podstawowy (Primary Key)
EF wyszukuje właściwość, której nazwa kończy się na **ID**. Wielkość liter nie ma znaczenia.
## Klucze obce (Foreign Key)
### Konwencja relacji Jeden-do-wielu
#### Konwencja 1
Encja zawiera navigation property.
``` csharp
public class Order
{
public int OrderId { get; set; }
public string OrderNumber { get; set; }
public Customer Customer { get; set; } // Navigation property
}
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
```
Zamówienie zawiera referencje do navigation property typu klient. EF utworzy shadow property CustomerId w modelu koncepcyjnym, które będzie mapowane do kolumny CustomerId w tabeli Orders.
#### Konwencja 2
Encja zawiera kolekcję.
``` csharp
public class Order
{
public int OrderId { get; set; }
public string OrderNumber { get; set; }
}
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List Orders { get; set; }
}
```
W bazie danych będzie taki sam rezultat jak w przypadku konwencji 1.
#### Konwencja 3
Relacja zawiera navigation property po obu stronach. W rezultacie otrzymujemy połączenie konwencji 1 i 2.
``` csharp
public class Order
{
public int OrderId { get; set; }
public string OrderNumber { get; set; }
public Customer Customer { get; set; } // Navigation property
}
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List Orders { get; set; }
}
```
#### Konwencja 4
Konwencja z uzyciem wlasciwosci foreign key
``` csharp
public class Order
{
public int OrderId { get; set; }
public string OrderNumber { get; set; }
public int CustomerId { get; set; } // Foreign key property
public Customer Customer { get; set; } // Navigation property
}
public class Customer
{
public int CustomerId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List Orders { get; set; }
}
```
### Konwencja relacji Jeden-do-jeden
``` csharp
public class Order
{
public int OrderId { get; set; }
public string OrderNumber { get; set; }
public Payment Payment { get; set; } // Navigation property
}
public class Payment
{
public int PaymentId { get; set; }
public decimal Amount { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
}
```
### Konwencja relacji wiele-do-wielu
``` csharp
public class User
{
public int Id { get; set; }
public string Login { get; set; }
public ICollection Roles { get; set; } // Navigation property
}
public class Role
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection Users { get; set; } // Navigation property
}
```
Zostanie automatycznie utworzona tabela pośrednia
## Konfiguracja relacji Jeden-do-wielu z uzyciem Fluent API
``` csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.HasOne()
.WithMany(c=>c.Orders)
.HasForeignKey(p=>p.CustomerId);
```
Alternatywnie mozna wyjsc od drugiej strony
``` csharp
modelBuilder.Entity()
.HasMany(c=>c.Orders)
.WithOne(o=>o.Customer)
.HasForeignKey(o=>o.CustomerId);
}
```
## Konfiguracja kaskadowego usuwania z Fluent API
``` csharp
modelBuilder.Entity()
.HasMany(c=>c.Orders)
.WithOne(o=>o.Customer)
.HasForeignKey(o=>o.CustomerId)
.OnDelete(DeleteBehavior.Cascade);
```
Rodzaje:
- Cascade - usuwa wszystkie encje wraz z encją nadrzędną
- ClientSetNull - klucze obce w encjach zaleznych będą ustawione na null
- Restrict - blokuje kaskadowe usuwanie
- SetNull - klucze obce w encjach zaleznych będą ustawione na null
## Konfiguracja jeden-do-jeden z Fluent API
``` csharp
modelBuilder.Entity()
.HasOne()
.WithOne(p=>p.Order)
.HasForeignKey(p=>p.PaymentId);
```
## Konfiguracja wiele-do-wiele z Fluent API
### Podstawowa konfiguracja
``` csharp
modelBuilder.Entity()
.HasMany(e => e.Roles)
.WithMany(e => e.Users);
```
Powstanie tabela pośrednicząca o nazwie UserRoles
Jeśli zamienimy miejscami pola otrzymamy inną nazwę tabeli:
``` csharp
modelBuilder.Entity()
.HasMany(e => e.Users)
.WithMany(e => e.Roles);
```
Powstanie tabela pośrednicząca o nazwie RoleUsers
### Własna nazwy tabeli
``` csharp
modelBuilder.Entity()
.HasMany(e => e.Roles)
.WithMany(e => e.Users)
.Map(m=>
{
m.ToTable("UsersInRoles");
});
```
Powstanie tabela pośrednicząca o nazwie UsersInRoles
### Własne klucze obce
``` csharp
modelBuilder.Entity()
.HasMany(e => e.Roles)
.WithMany(e => e.Users)
.Map(m=>
{
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
```
## Typ złożony (Complex Type)
Jeśli klasa nie ma klucza podstawowego.
# Śledzenie (Tracking)
## AutoDetectChanges
Domyślnie śledzenie zmian jest włączone. W celu zwiększenia wydajności, zwłaszcza przy dodawaniu dużej ilości encji warto wyłączyć automatyczne wykrywanie zmian:
~~~ csharp
using (var context = new MyContext())
{
context.Configuration.AutoDetectChangesEnabled = false;
}
~~~
Pamiętaj o wywołaniu metody _DetectChanges()_ przed _SaveChanges()_
Przykład:
~~~ csharp
using (var context = new MyContext())
{
using (var context = new MyContext())
{
try
{
context.Configuration.AutoDetectChangesEnabled = false;
foreach (var product in context.Products)
{
product.UnitPrice = 1.0m;
}
}
finally
{
context.Configuration.AutoDetectChangesEnabled = true;
}
context.ChangeTracker.DetectChanges();
context.SaveChanged();
}
}
~~~
### Lokalne wyłączenie śledzenia
~~~ csharp
using (var context = new MyContext())
{
var blogs = context.Customers
.AsNoTracking()
.ToList();
}
~~~
### Pobranie informacji o encjach
~~~ csharp
Console.WriteLine(
$"Tracked Entities: {context.ChangeTracker.Entries().Count()}");
foreach (var entry in context.ChangeTracker.Entries())
{
Console.WriteLine($"Entity: {entry.Entity.GetType().Name},
State: {entry.State.ToString()} ");
}
~~~
## Praca z odłączonymi encjami
### Attach()
Metoda *Attach()* przyłącza odłączony graf encji i zaczyna go śledzić.
Metoda *Attach()* ustawia główną encję na stan Added niezależnie od tego, czy posiada wartość klucza. Jeśli encje dzieci posiadają wartość klucza wówczas zaznaczane są jako *Unchanged*, a w przeciwnym razie jako *Added*.
``` csharp
context.Attach(entityGraph).State = state;
```
| Attach() | Root entity with Key value | Root Entity with Empty or CLR default value | Child Entity with Key value | Child Entity with empty or CLR default value |
|---|---|---|---|---|
| EntityState.Added | Added | Added | Unchanged | Added |
| EntityState.Modified | Modified | Exception | Unchanged | Added |
| EntityState.Deleted | Deleted | Exception | Unchanged | Added |
### Entry
``` csharp
context.Entry(order).State = EntityState.Modified
```
Wyrażenie przyłącza encję do kontekstu i ustawia stan na **Modified**. Ignoruje wszystkie pozostałe encje.
### Add()
Metody *DbContext.Add()* i *DbSet.Add()* przyłączają graf encji do kontekstu i ustawiają stan encji na **Added** niezależnie od tego czy posiadają wartość klucza czy też nie.
| Method | Root entity with/out Key value | Root entity with/out Key |
|---|---|---|
| DbContext.Add | Added | Added |
### Update()
Metoda *Update()* przyłącza graf encji do kontekstu i ustawia stan poszczególnych encji zależnie od tego czy jest ustawiona wartość klucza.
| Update() | Root entity with Key value | Root Entity with Empty or CLR default value | Child Entity with Key value | Child Entity with empty or CLR default value |
|---|---|---|---|---|
| DbContext.Update | Modified | Added | Modified | Added |
### Delete()
Metoda *Delete()* ustawia stan głównej encji na **Deleted**.
| Delete() | Root entity with Key value | Root Entity with Empty or CLR default value | Child Entity with Key value | Child Entity with empty or CLR default value |
|---|---|---|---|---|
| DbContext.Delete | Deleted | Exception | Unchanged | Added |
## Change Tracker
Odczytanie stanu encji
~~~ csharp
Trace.WriteLine(context.Entry(customer).State);
foreach (var property in context.Entry(customer).Properties)
{
Trace.WriteLine($"{property.Metadata.Name} {property.IsModified} {property.OriginalValue} -> {property.CurrentValue}");
}
~~~
## Surowy SQL
### Uruchomienie zapytania SQL i pobranie wyników
~~~ csharp
public IEnumerable Get(string lastname)
{
string sql = $"select * from dbo.customers where LastName = '{lastname}'";
return context.Database.SqlQuery(sql);
}
~~~
### Uruchomienie procedury składowanej
~~~ csharp
using (var context = new SampleContext())
{
var books = context.Database
.SqlQuery("GetAllCustomers")
.ToList();
}
~~~
### Uruchomienie procedury składowanej z parametrami
~~~ csharp
using (var context = new SampleContext())
{
var city = new SqlParameter("@City", "Warsaw");
var customers = context
.SqlQuery("GetCustomersByCity @City" , city)
.ToList();
}
~~~
### Typy anonimowe i pobieranie wartości
~~~ csharp
var orderHeaders = db.Database.SqlQuery(
@"select c.Name as CustomerName, o.DateCreated, sum(oi.Price) as TotalPrice,
count(oi.Price) as TotalItems
from OrderItems oi
inner join Orders o on oi.OrderId = o.OrderId
inner join Customers c on o.CustomerId = c.CustomerId
group by oi.OrderId, c.Name, o.DateCreated");
~~~
## Indeksy
~~~ csharp
class EmployeeConfiguration : EntityTypeConfiguration
{
public EmployeeConfiguration()
{
HasIndex(p => p.Email)
.IsUnique();
}
}
~~~
# Migracje
## Przydatne polecenia
- ``` Enable-Migrations ``` - włączenie migracji
- ``` Add-Migration {migration} ``` - utworzenie migracji
- ``` Add-Migration {migration} -force ``` - ponowne utworzenie migracji
- ``` Update-Database ``` - aktualizacja bazy danych do najnowszej wersji
- ``` Update-Database -script ``` - wygenerowanie skryptu do aktualizacji bazy danych do najnowszej wersji
- ``` Update-Database -verbose ``` - aktualizacja bazy danych do najnowszej wersji + wyświetlanie logu
- ``` Update-Database -TargetMigration: {migration} ``` - aktualizacja bazy danych do wskazanej migracji
- ``` Update-Database -SourceMigration: {migrationA} -TargetMigration: {migrationB} ``` - aktualizacja bazy danych
pomiędzy migracjami
- ``` Update-Database -TargetMigration: $InitialDatabase ``` - aktualizacja bazy danych do pustej bazy danych
pomiędzy migracjami
- ``` Update-Database -SourceMigration: $InitialDatabase -script``` - wygenerowanie kompletnego skryptu od pustej bazy danych
### Utworzenie triggera
1. Utwórz folder np. Scripts i plik OnDeleteOrderDetail.sql
~~~ sql
CREATE TRIGGER OnDeleteOrderDetail
ON [dbo].[OrderDetails]
FOR DELETE
AS
UPDATE [dbo].[Orders] SET ModifiedAt = getdate() WHERE Id = deleted.OrderId
~~~
2. Ustaw Build Action na Embedded Resource
3. Utwórz klasę migracji
~~~ csharp
public partial class AddTriggerOnDeleteOrderDetails : DbMigration
{
public override void Up()
{
SqlResource("MyApp.Scripts.201609301218380_AddTriggerOnDeleteOrderDetail_Up.sql", suppressTransaction: true);
}
public override void Down()
{
Sql("IF OBJECT_ID ('[OnDeleteOrderDetail]', 'TR') IS NOT NULL DROP TRIGGER OnDeleteOrderDetail");
}
}
~~~
### Utworzenie procedury składowanej
~~~ chsarp
public override void Up()
{
CreateStoredProcedure(
"MyStoredProcedure",
p => new
{
id = p.Int()
},
@"SELECT some-data FROM my-table WHERE id = @id"
);
}
public override void Down()
{
DropStoredProcedure("MyStoredProcedure");
}
~~~
## Inicjalizatory
### Wbudowane inicjalizatory
| Typ | Opis |
|---|---|
| CreateDatabaseIfNotExists | utwórz bazę danych jeśli nie istnieje |
| DropCreateDatabaseAlways | zawsze usuń i utwórz bazę danych |
| DropCreateDatabaseIfModelChanges | usuń i utwórz bazę danych jeśli nastąpiły zmiany w modelu |
### Ustawienie inicjalizatora w kodzie
~~~ csharp
public class MyContext : DbContext
{
public MyContext() : base("MyDbConnection")
{
Database.SetInitializer(new DropCreateDatabaseIfModelChanges());
}
}
~~~
### Ustawienie inicjalizatora w pliku konfiguracyjnym
~~~ xml
~~~
### Własny inicjalizator
~~~ csharp
public class MyDbInitializer : IDatabaseInitializer
{
public void MyDbInitializer(MyContext context)
{
if (!context.Database.Exists() || !context.Database.CompatibleWithModel(true))
{
context.Database.Delete();
context.Database.Create();
}
// context.Database.ExecuteSqlCommand("Custom SQL Command here");
}
}
~~~
### Wyłączenie inicjalizatorów
~~~ csharp
public MyContext() : base("MyDbConnection")
{
Database.SetInitializer(null);
}
~~~
### Rozszerzenie wbudowanego inicjalizatora
Przykład wypełnienia danych
~~~ csharp
public class MyDbInitializer : CreateDatabaseIfNotExists
{
private readonly CustomerFaker customerFaker;
public MyDbInitializer(CustomerFaker customerFaker)
{
this.customerFaker = customerFaker;
}
protected override void Seed(TransportContext context)
{
context.Customers.AddRange(customerFaker.Generate(1000));
base.Seed(context);
}
}
~~~
## Śledzenie zapytań SQL
### Logowanie
~~~ csharp
public MyContext()
{
this.Database.Log += Console.WriteLine;
}
~~~
### Formatowanie logów
Utworzenie własnego formattera
~~~ csharp
public class OneLineFormatter : DatabaseLogFormatter
{
public OneLineFormatter(DbContext context, Action writeAction)
: base(context, writeAction)
{
}
public override void LogCommand(
DbCommand command, DbCommandInterceptionContext interceptionContext)
{
Write(string.Format(
"Context '{0}' is executing command '{1}'{2}",
Context.GetType().Name,
command.CommandText.Replace(Environment.NewLine, ""),
Environment.NewLine));
}
public override void LogResult(
DbCommand command, DbCommandInterceptionContext interceptionContext)
{
}
}
~~~
Rejestracja:
~~~ csharp
public class MyDbConfiguration : DbConfiguration
{
public MyDbConfiguration()
{
SetDatabaseLogFormatter((context, writeAction) => new OneLineFormatter(context, writeAction));
}
}
~~~
## Ładowanie powiązanych encji
### Zachłanne ładowanie (Eager Loading)
#### Ładowanie powiązanej właściwości
~~~ csharp
using (var context = new RentContext())
{
var rentals = context.Vehicle
.Include(p => p.Owner)
.ToList();
}
~~~
#### Ładowanie zagnieżdżonych encji
~~~ csharp
using (var context = new RentContext())
{
var rentals = context.Vehicle
.Include(p => p.Owner).Select(b => b.Rentee).Include(b => b.Address);
.ToList();
}
~~~
### Leniwe ładowanie (Lazy Loading)
MyContext.cs
~~~ csharp
public MyContext()
: base("MyDbConnection")
{
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.ProxyCreationEnabled = true;
}
~~~
Właściwości muszą być oznaczone jako **publiczne** i **wirtualne**. W przeciwnym razie Lazy Loading nie będzie działać!
~~~ csharp
public class Vehicle : Base
{
public int Id { get; set; }
public virtual Employee Owner { get; set; }
public virtual ICollection Passangers { get; set; }
}
~~~
### Jawne ładowanie (Explicit Loading)
#### Ładowanie powiązanej encji
~~~ csharp
context.Entry(vehicle).Reference(p => p.Owner).Load();
~~~
### Ładowanie powiązanej kolekcji
~~~ csharp
context.Entry(vehicle).Collection(p => p.Passangers).Load();
~~~
### Filtrowanie ładowanej kolekcji
~~~ csharp
context.Entry(vehicle)
.Collection(p => p.Passangers)
.Query()
.Where(p=>p.Gender = Gender.Female)
.Load();
~~~
## Transakcje
### Transakcje bazy danych
~~~ csharp
private void Save(Order order)
{
using (var context = new MyContext())
using (var transaction = context.Database.BeginTransaction())
{
try
{
context.Orders.Add(order);
context.SaveChanges();
context.Customers.Add(order.Customer);
context.SaveChanges();
transaction.Commit();
}
catch(Exception)
{
transaction.Rollback();
}
}
}
~~~
### Rozproszone transakcje
Dodaj referencję do _System.Transactions_
~~~ csharp
private static void Save(Order oder)
{
using (var scope = new TransactionScope())
{
using (var context1 = new OrdersContext())
{
context1.Orders.Add(order);
context1.SaveChanges();
}
using (var context2 = new CustomersContext())
{
context2.Customers.Add(order.Customer);
context2.SaveChanges();
}
scope.Complete();
}
}
~~~
uwaga: w przypadku wykorzystania transakcji w metodzie asynchronicznej otrzymamy błąd.
Dlatego należy dodać parametr w konstruktorze:
~~~ csharp
var transactionScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)
~~~
## Współbieżność
### Token
#### Konfiguracja za pomocą atrybutu
~~~ csharp
public class Employee
{
public int Id { get; set; }
[ConcurrencyCheck]
public string FirstName { get; set; }
public string LastName { get; set; }
}
~~~
#### Konfiguracja za pomocą FluentAPI
~~~ csharp
class EmployeeConfiguration : EntityTypeConfiguration
{
public EmployeeConfiguration()
{
Property(p => p.FirstName)
.IsConcurrencyToken();
}
}
~~~
#### Wykrywanie kolizji
~~~ csharp
private static void ConcurencyTest()
{
using (var context = new MyContext())
{
var employee = context.Empoyees.Find(1);
employee.FirstName = "John";
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
ex.Entries.Single().Reload();
}
} while (saveFailed);
}
}
~~~
### RowVersion
#### Konfiguracja za pomocą atrybutu
~~~ csharp
public class Employee
{
[Timestamp]
public byte[] RowVersion { get; set; }
}
~~~
#### Konfiguracja za pomocą FluentApi
~~~ csharp
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public byte[] RowVersion { get; set; }
}
~~~
~~~ csharp
class EmployeeConfiguration : EntityTypeConfiguration
{
public EmployeeConfiguration()
{
Property(p => p.RowVersion)
.IsConcurrencyToken()
.IsRowVersion();
}
}
~~~
## Mapowanie operacji CRUD na procedury składowane
### Konfiguracja FluentApi
~~~ csharp
class EmployeeConfiguration : EntityTypeConfiguration
{
public EmployeeConfiguration()
{
MapToStoredProcedures();
}
}
~~~
Utworzone zostaną procedury składowane.
### Modyfikacja nazw
Modyfikacja nazw procedur składowanych
~~~ csharp
class EmployeeConfiguration : EntityTypeConfiguration
{
public EmployeeConfiguration()
{
MapToStoredProcedures(s =>
{
s.Update(u => u.HasName("modify_employee"));
s.Delete(d => d.HasName("delete_employee"));
s.Insert(i => i.HasName("insert_employee"));
});
}
}
~~~
## Odłączone encje
### Zapis odłączonej encji z użyciem biblioteki GraphDiff
Instalacja biblioteki
~~~ bash
PM> Install-Package RefactorThis.GraphDiff
~~~
~~~ csharp
private static void GraphDiffTest()
{
Artist artist = new Artist { ArtistId = 1, FirstName = "The Artist Formerly Known as Prince" };
using (var context = new MusicStoreContext())
{
context.UpdateGraph(artist, map => map
.OwnedCollection(p => p.Albums));
context.SaveChanges();
}
}
~~~
http://blog.brentmckendrick.com/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a-graph-of-detached-entities/