Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Při vytváření instance entity je možné definovat konstruktor s parametry a volat tento konstruktor EF Core. Parametry konstruktoru mohou být svázány s mapovanými vlastnostmi nebo s různými druhy služeb, které usnadňují chování, jako je opožděné načítání.
Poznámka:
V současné době platí, že všechny vazby konstruktoru jsou podle konvence. Konfigurace konkrétních konstruktorů, které se mají použít, se plánuje pro budoucí vydání.
Vazba na mapované vlastnosti
Představte si typický model blogu nebo příspěvku:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Když EF Core vytvoří instance těchto typů, například pro výsledky dotazu, nejprve zavolá výchozí konstruktor bez parametrů a pak nastaví každou vlastnost na hodnotu z databáze. Pokud však EF Core najde parametrizovaný konstruktor s názvy a typy parametrů, které odpovídají mapovaným vlastnostem, bude místo toho volat parametrizovaný konstruktor s hodnotami pro tyto vlastnosti a nenastaví jednotlivé vlastnosti explicitně. Například:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Několik věcí, které si poznamenejte:
- Ne všechny vlastnosti musí mít parametry konstruktoru. Vlastnost Post.Content například není nastavena žádným parametrem konstruktoru, takže EF Core ji nastaví po volání konstruktoru normálním způsobem.
- Typy a názvy parametrů se musí shodovat s typy a názvy vlastností, s výjimkou toho, že vlastnosti mohou být v Pascal case, zatímco parametry jsou v camel case.
- EF Core nemůže nastavit navigační vlastnosti (například blog nebo příspěvky výše) pomocí konstruktoru.
- Konstruktor může být veřejný, soukromý nebo může mít jakoukoli jinou přístupnost. Lazy-loading proxy servery však vyžadují, aby konstruktor musí být přístupný z třídy proxy, která dědí. Obvykle to znamená, že je buď veřejná, nebo chráněná.
Vlastnosti pouze pro čtení
Jakmile jsou vlastnosti nastaveny prostřednictvím konstruktoru, může dávat smysl, aby některé z nich byly jen pro čtení. EF Core to podporuje, ale je potřeba se podívat na některé věci:
- Vlastnosti bez setter nejsou mapovány podle konvence. (Při tom se obvykle mapují vlastnosti, které by se neměly mapovat, například vypočítané vlastnosti.)
- Použití automaticky generovaných hodnot klíče vyžaduje vlastnost klíče, která je určená pro čtení i zápis, protože při vkládání nových entit je potřeba nastavit hodnotu klíče generátorem klíčů.
Snadný způsob, jak se těmto věcem vyhnout, je používat soukromé settery. Například:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Author { get; private set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; private set; }
public string Title { get; private set; }
public string Content { get; set; }
public DateTime PostedOn { get; private set; }
public Blog Blog { get; set; }
}
EF Core vidí vlastnost s privátním setterem jako pro čtení i zápis, což znamená, že všechny vlastnosti jsou mapovány jako předtím a klíč se stále dá vygenerovat.
Alternativou k použití privátních setter je vytvoření vlastností jen pro čtení a přidání explicitního mapování v OnModelCreating. Podobně lze některé vlastnosti zcela odebrat a nahradit pouze poli. Představte si například tyto typy entit:
public class Blog
{
private int _id;
public Blog(string name, string author)
{
Name = name;
Author = author;
}
public string Name { get; }
public string Author { get; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
private int _id;
public Post(string title, DateTime postedOn)
{
Title = title;
PostedOn = postedOn;
}
public string Title { get; }
public string Content { get; set; }
public DateTime PostedOn { get; }
public Blog Blog { get; set; }
}
A tato konfigurace v OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Author);
b.Property(e => e.Name);
});
modelBuilder.Entity<Post>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Title);
b.Property(e => e.PostedOn);
});
}
Poznámky:
- Klíč "vlastnost" je nyní proměnlivým polem. Nejedná se o
readonlypole, aby bylo možné použít klíče vygenerované v úložišti. - Ostatní vlastnosti jsou vlastnosti jen pro čtení nastavené pouze v konstruktoru.
- Pokud je hodnota primárního klíče výhradně nastavena EF, nebo je přečtena z databáze, není nutné ji zahrnout do konstruktoru. Tím ponecháte klíč "vlastnost" jako jednoduché pole a je jasné, že by se nemělo explicitně nastavovat při vytváření nových blogů nebo příspěvků.
Poznámka:
Výsledkem tohoto kódu bude upozornění kompilátoru 169 označující, že pole se nikdy nepoužívá. To je možné ignorovat, protože EF Core ve skutečnosti používá pole extralinguistickým způsobem.
Vkládání služeb
EF Core může také vložit "služby" do konstruktoru typu entity. Například lze vložit následující:
-
DbContext– aktuální kontextová instance, kterou lze také zadat jako odvozený typ DbContext. -
ILazyLoader- lazy-loading služba—viz dokumentaci lazy-loadingu pro více podrobností -
Action<object, string>- delegát pro líné načítání—podívejte se na dokumentaci k línému načítání pro více podrobností -
IEntityType– metadata EF Core přidružená k tomuto typu entity
Poznámka:
V současné době lze do EF Core injektovat pouze služby, které jsou mu známy. Zvažuje se podpora pro vkládání aplikačních služeb v budoucím vydání.
Vložený dbContext lze například použít k selektivnímu přístupu k databázi a získat informace o souvisejících entitách bez jejich načtení. V následujícím příkladu se používá k získání počtu příspěvků v blogu bez načtení příspěvků:
public class Blog
{
public Blog()
{
}
private Blog(BloggingContext context)
{
Context = context;
}
private BloggingContext Context { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; set; }
public int PostsCount
=> Posts?.Count
?? Context?.Set<Post>().Count(p => Id == EF.Property<int?>(p, "BlogId"))
?? 0;
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Několik věcí, které byste si o tom mohli všimnout:
- Konstruktor je privátní, protože je volán pouze knihovnou EF Core, a existuje další veřejný konstruktor pro obecné použití.
- Kód používající vloženou službu (tj. kontext) je defenzivní proti tomu
null, aby zpracovával případy, kdy EF Core nevytvořuje instanci. - Protože služba je uložena ve vlastnosti pro čtení a zápis, bude resetována, když je entita připojena k nové instanci kontextu.
Výstraha
Vkládání DbContextu tímto způsobem se často považuje za antivzor, protože spojuje typy entit přímo s EF Core. Pečlivě zvažte všechny možnosti, než použijete službovou injekci.