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.
Om du kör en LINQ-fråga direkt mot en DbSet skickas alltid en fråga till databasen, men du kan komma åt de data som för närvarande finns i minnet med hjälp av egenskapen DbSet.Local. Du kan också komma åt den extra information som EF spårar om dina entiteter med hjälp av metoderna DbContext.Entry och DbContext.ChangeTracker.Entries. De tekniker som visas i det här avsnittet gäller lika för modeller som skapats med Code First och EF Designer.
Använda Lokal för att titta på lokala data
Egenskapen Lokal för DbSet ger enkel åtkomst till entiteterna i uppsättningen som för närvarande spåras av kontexten och som inte har markerats som Borttagna. Åtkomst till den lokala egenskapen gör aldrig att en fråga skickas till databasen. Det innebär att den vanligtvis används när en fråga redan har utförts. Load-tilläggsmetoden kan användas för att köra en fråga så att sammanhanget kan spåra resultaten. Som exempel:
using (var context = new BloggingContext())
{
// Load all blogs from the database into the context
context.Blogs.Load();
// Add a new blog to the context
context.Blogs.Add(new Blog { Name = "My New Blog" });
// Mark one of the existing blogs as Deleted
context.Blogs.Remove(context.Blogs.Find(1));
// Loop over the blogs in the context.
Console.WriteLine("In Local: ");
foreach (var blog in context.Blogs.Local)
{
Console.WriteLine(
"Found {0}: {1} with state {2}",
blog.BlogId,
blog.Name,
context.Entry(blog).State);
}
// Perform a query against the database.
Console.WriteLine("\nIn DbSet query: ");
foreach (var blog in context.Blogs)
{
Console.WriteLine(
"Found {0}: {1} with state {2}",
blog.BlogId,
blog.Name,
context.Entry(blog).State);
}
}
Om vi hade två bloggar i databasen – "ADO.NET Blogg" med ett BlogId på 1 och "The Visual Studio Blog" med ett BlogId på 2 – kan vi förvänta oss följande utdata:
In Local:
Found 0: My New Blog with state Added
Found 2: The Visual Studio Blog with state Unchanged
In DbSet query:
Found 1: ADO.NET Blog with state Deleted
Found 2: The Visual Studio Blog with state Unchanged
Detta illustrerar tre punkter:
- Den nya bloggen "Min nya blogg" ingår i den lokala samlingen även om den ännu inte har sparats i databasen. Den här bloggen har en primärnyckel på noll eftersom databasen ännu inte har genererat en verklig nyckel för entiteten.
- "ADO.NET Blog" ingår inte i den lokala samlingen även om den fortfarande spåras av kontexten. Det beror på att vi har tagit bort den från DbSet och därmed markerat den som borttagen.
- När DbSet används för att utföra en fråga som bloggen har markerats för borttagning (ADO.NET blogg) ingår i resultatet och den nya bloggen (Min nya blogg) som ännu inte har sparats i databasen ingår inte i resultaten. Det beror på att DbSet utför en fråga mot databasen och de resultat som returneras återspeglar alltid vad som finns i databasen.
Använda Lokal för att lägga till och ta bort entiteter från kontexten
Egenskapen Lokal på DbSet returnerar en ObservableCollection med händelser kopplade så att den förblir synkroniserad med innehållet i kontexten. Det innebär att entiteter kan läggas till eller tas bort från antingen den lokala samlingen eller DbSet. Det innebär också att frågor som för in nya entiteter i kontexten resulterar i att den lokala samlingen uppdateras med dessa entiteter. Som exempel:
using (var context = new BloggingContext())
{
// Load some posts from the database into the context
context.Posts.Where(p => p.Tags.Contains("entity-framework")).Load();
// Get the local collection and make some changes to it
var localPosts = context.Posts.Local;
localPosts.Add(new Post { Name = "What's New in EF" });
localPosts.Remove(context.Posts.Find(1));
// Loop over the posts in the context.
Console.WriteLine("In Local after entity-framework query: ");
foreach (var post in context.Posts.Local)
{
Console.WriteLine(
"Found {0}: {1} with state {2}",
post.Id,
post.Title,
context.Entry(post).State);
}
var post1 = context.Posts.Find(1);
Console.WriteLine(
"State of post 1: {0} is {1}",
post1.Name,
context.Entry(post1).State);
// Query some more posts from the database
context.Posts.Where(p => p.Tags.Contains("asp.net")).Load();
// Loop over the posts in the context again.
Console.WriteLine("\nIn Local after asp.net query: ");
foreach (var post in context.Posts.Local)
{
Console.WriteLine(
"Found {0}: {1} with state {2}",
post.Id,
post.Title,
context.Entry(post).State);
}
}
Förutsatt att vi hade några inlägg taggade med "entity-framework" och "asp.net" kan utdata se ut ungefär så här:
In Local after entity-framework query:
Found 3: EF Designer Basics with state Unchanged
Found 5: EF Code First Basics with state Unchanged
Found 0: What's New in EF with state Added
State of post 1: EF Beginners Guide is Deleted
In Local after asp.net query:
Found 3: EF Designer Basics with state Unchanged
Found 5: EF Code First Basics with state Unchanged
Found 0: What's New in EF with state Added
Found 4: ASP.NET Beginners Guide with state Unchanged
Detta illustrerar tre punkter:
- Det nya inlägget "Nyheter i EF" som lades till i den lokala samlingen spåras av kontexten i tillståndet Lägg till. Den infogas därför i databasen när SaveChanges anropas.
- Inlägget som togs bort från den lokala samlingen (EF Beginners Guide) har nu markerats som borttaget i kontexten. Den tas därför bort från databasen när SaveChanges anropas.
- Det ytterligare inlägget (ASP.NET nybörjarguiden) som läses in i kontexten med den andra frågan läggs automatiskt till i den lokala samlingen.
En sista sak att notera om Local är att, eftersom det är en ObservableCollection, är prestandan inte bra för ett stort antal entiteter. Om du har att göra med tusentals entiteter i din kontext kanske det inte är lämpligt att använda Local.
Använda local för WPF-databindning
Den lokala egenskapen på DbSet kan användas direkt för databindning i ett WPF-program eftersom det är en instans av ObservableCollection. Enligt beskrivningen i föregående avsnitt innebär det att det automatiskt förblir synkroniserat med innehållet i kontexten och att innehållet i kontexten automatiskt förblir synkroniserat med det. Observera att du måste fylla i den lokala samlingen i förväg med data för att det ska finnas något att binda till eftersom Local aldrig orsakar en databasfråga.
Det här är inte en lämplig plats för ett fullständigt WPF-databindningsexempel, men de viktigaste elementen är:
- Konfigurera en bindningskälla
- Bind den till egenskapen 'Local' i din uppsättning
- Befolka Lokal med hjälp av en fråga till databasen.
WPF-bindning till navigeringsegenskaper
Om du utför huvud-/detaljdatabindning kanske du vill binda detaljvyn till en navigeringsegenskap för en av dina entiteter. Ett enkelt sätt att få det här att fungera är att använda en ObservableCollection för navigeringsegenskapen. Som exempel:
public class Blog
{
private readonly ObservableCollection<Post> _posts =
new ObservableCollection<Post>();
public int BlogId { get; set; }
public string Name { get; set; }
public virtual ObservableCollection<Post> Posts
{
get { return _posts; }
}
}
Använda Local för att rensa entiteter i SaveChanges
I de flesta fall markeras entiteter som tagits bort från en navigeringsegenskap inte automatiskt som borttagna i kontexten. Om du till exempel tar bort ett Post-objekt från samlingen Blog.Posts tas det inlägget inte bort automatiskt när SaveChanges anropas. Om du vill att den ska tas bort kan du behöva hitta dessa dinglande entiteter och markera dem som borttagna innan du anropar SaveChanges eller som en del av en åsidosatt SaveChanges. Som exempel:
public override int SaveChanges()
{
foreach (var post in this.Posts.Local.ToList())
{
if (post.Blog == null)
{
this.Posts.Remove(post);
}
}
return base.SaveChanges();
}
Koden ovan använder den lokala samlingen för att hitta alla inlägg och markerar alla som inte har en bloggreferens som borttagen. ToList-anropet krävs eftersom listan annars ändras av Remove-anropet medan den itereras över. I de flesta andra situationer kan du fråga direkt mot den lokala egenskapen utan att först använda ToList.
Använda Local och ToBindingList för databindning i Windows Forms
Windows Forms stöder inte databindning med fullständig återgivning med ObservableCollection direkt. Du kan dock fortfarande använda egenskapen DbSet Local för databindning för att få alla fördelar som beskrivs i föregående avsnitt. Detta uppnås via metoden ToBindingList-tillägg som skapar en IBindingList-implementering som backas upp av Local ObservableCollection.
Det här är inte en lämplig plats för ett fullständigt databindningsexempel för Windows Forms, men de viktigaste elementen är:
- Konfigurera en objektbindningskälla
- Binda den till Local-egenskapen för din samling med hjälp av Local.ToBindingList()
- Fyll i lokala data med en fråga till databasen
Få detaljerad information om spårade entiteter
Många av exemplen i den här serien använder metoden Entry för att returnera en DbEntityEntry-instans för en entitet. Det här objektet fungerar sedan som startpunkt för att samla in information om entiteten, såsom dess aktuella tillstånd, samt för att utföra operationer, till exempel att explicit ladda en relaterad entitet.
Metoderna Entries returnerar DbEntityEntry-objekt för många eller alla entiteter som spåras av kontexten. På så sätt kan du samla in information eller utföra åtgärder på många entiteter i stället för bara en enda post. Som exempel:
using (var context = new BloggingContext())
{
// Load some entities into the context
context.Blogs.Load();
context.Authors.Load();
context.Readers.Load();
// Make some changes
context.Blogs.Find(1).Title = "The New ADO.NET Blog";
context.Blogs.Remove(context.Blogs.Find(2));
context.Authors.Add(new Author { Name = "Jane Doe" });
context.Readers.Find(1).Username = "johndoe1987";
// Look at the state of all entities in the context
Console.WriteLine("All tracked entities: ");
foreach (var entry in context.ChangeTracker.Entries())
{
Console.WriteLine(
"Found entity of type {0} with state {1}",
ObjectContext.GetObjectType(entry.Entity.GetType()).Name,
entry.State);
}
// Find modified entities of any type
Console.WriteLine("\nAll modified entities: ");
foreach (var entry in context.ChangeTracker.Entries()
.Where(e => e.State == EntityState.Modified))
{
Console.WriteLine(
"Found entity of type {0} with state {1}",
ObjectContext.GetObjectType(entry.Entity.GetType()).Name,
entry.State);
}
// Get some information about just the tracked blogs
Console.WriteLine("\nTracked blogs: ");
foreach (var entry in context.ChangeTracker.Entries<Blog>())
{
Console.WriteLine(
"Found Blog {0}: {1} with original Name {2}",
entry.Entity.BlogId,
entry.Entity.Name,
entry.Property(p => p.Name).OriginalValue);
}
// Find all people (author or reader)
Console.WriteLine("\nPeople: ");
foreach (var entry in context.ChangeTracker.Entries<IPerson>())
{
Console.WriteLine("Found Person {0}", entry.Entity.Name);
}
}
Du kommer att märka att vi introducerar en klass för författare och läsare i exemplet – båda dessa klasser implementerar IPerson-gränssnittet.
public class Author : IPerson
{
public int AuthorId { get; set; }
public string Name { get; set; }
public string Biography { get; set; }
}
public class Reader : IPerson
{
public int ReaderId { get; set; }
public string Name { get; set; }
public string Username { get; set; }
}
public interface IPerson
{
string Name { get; }
}
Anta att vi har följande data i databasen:
Blogg med BlogId = 1 och Namn = "ADO.NET Blogg"
Blogg med BlogId = 2 och Namn = "Visual Studio-bloggen"
Blogg med BlogId = 3 och Name = '.NET Framework Blog'
Författare med AuthorId = 1 och Namn = "Joe Bloggs"
Läsare med ReaderId = 1 och Namn = 'John Doe'
Utdata från körning av koden skulle vara:
All tracked entities:
Found entity of type Blog with state Modified
Found entity of type Blog with state Deleted
Found entity of type Blog with state Unchanged
Found entity of type Author with state Unchanged
Found entity of type Author with state Added
Found entity of type Reader with state Modified
All modified entities:
Found entity of type Blog with state Modified
Found entity of type Reader with state Modified
Tracked blogs:
Found Blog 1: The New ADO.NET Blog with original Name ADO.NET Blog
Found Blog 2: The Visual Studio Blog with original Name The Visual Studio Blog
Found Blog 3: .NET Framework Blog with original Name .NET Framework Blog
People:
Found Person John Doe
Found Person Joe Bloggs
Found Person Jane Doe
De här exemplen illustrerar flera punkter:
- Entries-metoderna returnerar poster för entiteter i alla tillstånd, inklusive raderade. Jämför detta med Lokalt som exkluderar borttagna entiteter.
- Poster för alla entitetstyper returneras när den icke-generiska metoden Entries används. När metoden generiska poster används returneras poster endast för entiteter som är instanser av den generiska typen. Detta användes ovan för att hämta inlägg för alla bloggar. Den användes också för att hämta poster för alla entiteter som implementerar IPerson. Detta visar att den generiska typen inte behöver vara en faktisk entitetstyp.
- LINQ till objekt kan användas för att filtrera resultatet som returneras. Detta användes ovan för att hitta entiteter av alla typer så länge dessa har modifierats.
Observera att DbEntityEntry-instanser alltid innehåller en icke-null-entitet. Relationsposter och stub-poster representeras inte som DbEntityEntry-instanser, så det finns inget behov av att filtrera efter dessa.