Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Det är möjligt att definiera en konstruktor med parametrar och låta EF Core anropa den här konstruktorn när du skapar en instans av entiteten. Konstruktorparametrarna kan bindas till mappade egenskaper eller till olika typer av servicekomponenter för att underlätta beteenden som fördröjd inläsning.
Anmärkning
För närvarande är all konstruktorbindning enligt konvention. Konfiguration av specifika konstruktorer som ska användas planeras för en framtida version.
Bindning till mappade egenskaper
Överväg en typisk Blogg/Post-modell:
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; }
}
När EF Core skapar instanser av dessa typer, till exempel för resultatet av en fråga, anropas först den parameterlösa standardkonstruktorn och anger sedan varje egenskap till värdet från databasen. Men om EF Core hittar en parametriserad konstruktor med parameternamn och typer som matchar egenskaperna för mappade egenskaper anropas i stället den parametriserade konstruktorn med värden för dessa egenskaper och anger inte varje egenskap explicit. Som exempel:
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ågra saker att notera:
- Alla egenskaper behöver inte ha konstruktorparametrar. Egenskapen Post.Content anges till exempel inte av någon konstruktorparameter, så EF Core anger den efter att ha anropat konstruktorn på vanligt sätt.
- Parametertyperna och namnen måste matcha egenskapstyper och namn, förutom att egenskaperna kan använda PascalCase medan parametrarna använder camelCase.
- EF Core kan inte ange navigeringsegenskaper (till exempel blogg eller inlägg ovan) med hjälp av en konstruktor.
- Konstruktorn kan vara offentlig, privat eller ha någon annan tillgänglighet. Proxyservrar med lat inläsning kräver dock att konstruktorn är tillgänglig från proxyklassen som ärver. Vanligtvis innebär det att göra det antingen offentligt eller skyddat.
Skrivskyddade egenskaper
När egenskaper sätts via konstruktorn kan det vara bra att göra vissa av dem skrivskyddade. EF Core stöder detta, men det finns några saker att hålla utkik efter:
- Egenskaper utan setters mappas inte av konventionen. (Detta tenderar att mappa egenskaper som inte ska mappas, till exempel beräknade egenskaper.)
- Om du använder automatiskt genererade nyckelvärden krävs en nyckelegenskap som är skrivskyddad, eftersom nyckelvärdet måste anges av nyckelgeneratorn när nya entiteter infogas.
Ett enkelt sätt att undvika dessa saker är att använda privata setters. Som exempel:
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 betraktar en egenskap med en privat setter som läs- och skrivbar, vilket innebär att alla egenskaper mappas som tidigare och att nyckeln fortfarande kan genereras av butiken.
Ett alternativ till att använda privata setters är att göra egenskaperna skrivskyddade och lägga till mer explicit mappning i OnModelCreating. På samma sätt kan vissa egenskaper tas bort helt och ersättas med endast fält. Tänk dig till exempel följande entitetstyper:
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; }
}
Och den här konfigurationen i 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);
});
}
Saker att notera:
- "Nyckeln "egenskap" har nu blivit ett fält." Det är inte ett
readonlyfält så att butiksgenererade nycklar kan användas. - De andra egenskaperna är skrivskyddade egenskaper som endast anges i konstruktorn.
- Om det primära nyckelvärdet bara anges av EF eller läss från databasen behöver du inte ta med det i konstruktorn. Detta lämnar nyckeln "egenskap" som ett enkelt fält och gör det tydligt att den inte bör anges uttryckligen när du skapar nya bloggar eller inlägg.
Anmärkning
Den här koden resulterar i kompilatorvarningen "169" som anger att fältet aldrig används. Detta kan ignoreras eftersom EF Core i själva verket använder fältet på ett extralinguistiskt sätt.
Mata in tjänster
EF Core kan också mata in "tjänster" i en entitetstyps konstruktor. Följande kan till exempel matas in:
-
DbContext– den aktuella kontextinstansen, som också kan skrivas som din härledda DbContext-typ -
ILazyLoader– lazy-loading-tjänsten – se dokumentationen om lata inläsningar för mer information -
Action<object, string>- en laddningsdelegat med lat inläsning – se dokumentation om lat inläsning för mer information -
IEntityType– EF Core-metadata som är associerade med den här entitetstypen
Anmärkning
För närvarande kan endast tjänster som är kända av EF Core matas in. Stöd för att mata in programtjänster övervägs för en framtida version.
Till exempel kan en inmatad DbContext användas för att selektivt få åtkomst till databasen för att hämta information om relaterade entiteter utan att läsa in alla. I exemplet nedan används detta för att hämta antalet inlägg i en blogg utan att läsa in inläggen:
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ågra saker att märka om detta:
- Konstruktorn är privat eftersom den bara anropas av EF Core, och det finns en annan offentlig konstruktor för allmän användning.
- Koden som använder den inmatade tjänsten (dvs. kontexten) är defensiv mot att den
nullska hantera fall där EF Core inte skapar instansen. - Eftersom tjänsten lagras i en läs-/skrivegenskap återställs den när entiteten är kopplad till en ny kontextinstans.
Varning
Att mata in DbContext så här anses ofta vara ett antimönster eftersom det kopplar dina entitetstyper direkt till EF Core. Tänk noga på alla alternativ innan du använder tjänsteinjektion på detta sätt.