Místní data

Spuštění dotazu LINQ přímo na DbSet vždy odešle dotaz do databáze, ale můžete získat přístup k datům, která jsou aktuálně v paměti pomocí DbSet.Local vlastnost. K dalším informacím, které EF sleduje, můžete také přistupovat k entitám pomocí metod DbContext.Entry a DbContext.ChangeTracker.Entrys. Techniky uvedené v tomto tématu jsou rovnocenné pro modely vytvořené pomocí Code First a EF Designeru.

Použití místního prostředí k zobrazení místních dat

Vlastnost Local dbSet poskytuje jednoduchý přístup k entitě sady, které jsou aktuálně sledovány kontextem a nebyly označeny jako Odstraněno. Přístup k místní vlastnosti nikdy nezpůsobí odeslání dotazu do databáze. To znamená, že se obvykle používá po provedení dotazu. Metodu rozšíření Load lze použít k provedení dotazu, aby kontext sleduje výsledky. Příklad:

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);
    }
}

Pokud bychom měli v databázi dva blogy – "ADO.NET Blog" s Id blogu 1 a 'The Visual Studio Blog' with a BlogId of 2 - můžeme očekávat následující výstup:

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

To znázorňuje tři body:

  • Nový blog "Můj nový blog" je součástí místní kolekce, i když ještě nebyl uložen do databáze. Tento blog má primární klíč nuly, protože databáze ještě negenerovala skutečný klíč entity.
  • Blog ADO.NET není součástí místní kolekce, i když je stále sledován kontextem. Je to proto, že jsme ji odebrali z dbSet a označili ji jako odstraněnou.
  • Pokud se dbSet používá k provedení dotazu, který je označen k odstranění (ADO.NET Blog), je součástí výsledků a nový blog (Můj nový blog), který ještě nebyl uložen do databáze, není zahrnut do výsledků. Důvodem je to, že DbSet provádí dotaz na databázi a vrácené výsledky vždy odrážejí, co je v databázi.

Přidání a odebrání entit z kontextu pomocí místního prostředí

Vlastnost Local v DbSet vrátí ObservableCollection s událostmi, které jsou připojeny tak, aby zůstaly synchronizované s obsahem kontextu. To znamená, že entity mohou být přidány nebo odebrány z místní kolekce nebo DbSet. Také to znamená, že dotazy, které do kontextu přinesou nové entity, způsobí aktualizaci místní kolekce s těmito entitami. Příklad:

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);
    }
}

Za předpokladu, že jsme měli několik příspěvků označených jako entity-framework a "asp.net", výstup může vypadat nějak takto:

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

To znázorňuje tři body:

  • Nový příspěvek "What's New in EF" (Co je nového v EF), který byl přidán do místní kolekce, se bude sledovat kontextem v přidaném stavu. Proto se vloží do databáze při zavolání SaveChanges.
  • Příspěvek, který byl odebrán z místní kolekce (EF Beginners Guide) je nyní označen jako odstraněný v kontextu. Proto se odstraní z databáze při zavolání SaveChanges.
  • Další příspěvek (průvodce ASP.NET začátečníky) načtený do kontextu s druhým dotazem se automaticky přidá do místní kolekce.

Jedním z posledních věcí, které je třeba poznamenat o Local je, že je to observableCollection výkon není skvělý pro velký počet entit. Proto pokud pracujete s tisíci entit ve vašem kontextu, nemusí být vhodné použít Místní.

Použití místního prostředí pro datovou vazbu WPF

Vlastnost Local v DbSet lze použít přímo pro datovou vazbu v aplikaci WPF, protože je to instance ObservableCollection. Jak je popsáno v předchozích částech, znamená to, že se automaticky synchronizuje s obsahem kontextu a obsah kontextu se s ním automaticky synchronizuje. Mějte na paměti, že je nutné předem naplnit místní kolekci daty, aby bylo možné vytvořit vazbu na cokoli, protože místní dotaz nikdy nezpůsobí databázový dotaz.

Toto není vhodné místo pro úplnou ukázku datové vazby WPF, ale klíčové prvky jsou:

  • Nastavení zdroje vazby
  • Vytvoření vazby k místní vlastnosti sady
  • Naplňte místní pomocí dotazu na databázi.

Vazba WPF na navigační vlastnosti

Pokud provádíte vazby dat master/detail, můžete chtít vytvořit vazbu zobrazení podrobností na navigační vlastnost jedné z entit. Snadný způsob, jak tuto práci provést, je použít ObservableCollection pro navigační vlastnost. Příklad:

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; }
    }
}

Vyčištění entit v saveChanges pomocí místního nastavení

Ve většině případů entity odebrané z navigační vlastnosti nebudou automaticky označeny jako odstraněné v kontextu. Pokud například odeberete objekt Post z kolekce Blog.Posts, tento příspěvek se při zavolání SaveChanges automaticky neodstraní. Pokud ho potřebujete odstranit, možná budete muset tyto propojené entity najít a před voláním saveChanges nebo jako součást přepsání SaveChanges je označit jako odstraněné. Příklad:

public override int SaveChanges()
{
    foreach (var post in this.Posts.Local.ToList())
    {
        if (post.Blog == null)
        {
            this.Posts.Remove(post);
        }
    }

    return base.SaveChanges();
}

Výše uvedený kód používá místní kolekci k vyhledání všech příspěvků a označení všech, které nemají odkaz na blog jako odstraněný. Volání ToList je vyžadováno, protože jinak bude kolekce změněna voláním Remove během vytváření výčtu. Ve většině jiných situací můžete dotazovat přímo na vlastnost Local bez použití ToList nejprve.

Použití local a ToBindingList pro model Windows Forms datovou vazbu

model Windows Forms nepodporuje úplnou věrnou datovou vazbu pomocí aplikace ObservableCollection přímo. Stále však můžete použít vlastnost DbSet Local pro datové vazby k získání všech výhod popsaných v předchozích částech. To je dosaženo prostřednictvím ToBindingList rozšíření metoda, která vytvoří IBindingList implementace zálohované Local ObservableCollection.

Toto není vhodné místo pro úplnou ukázku datové vazby model Windows Forms, ale klíčové prvky jsou:

  • Nastavení zdroje vazby objektu
  • Vytvořte vazbu na místní vlastnost vaší sady pomocí Local.ToBindingList()
  • Naplnění místního pomocí dotazu do databáze

Získání podrobných informací o sledovaných entitách

Mnoho příkladů v této řadě používá Metodu Entry k vrácení DbEntityEntry instance entity. Tento vstupní objekt pak funguje jako výchozí bod pro shromažďování informací o entitě, jako je její aktuální stav, a také pro provádění operací s entitou, jako je explicitní načtení související entity.

Metody Entries vrací DbEntityEntry objekty pro mnoho nebo všechny entity sledované kontextem. Díky tomu můžete shromažďovat informace nebo provádět operace s mnoha entitami, nikoli pouze s jednou položkou. Příklad:

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);
    }
}

Všimněte si, že do příkladu představujeme třídu Author a Reader – obě tyto třídy implementují rozhraní IPerson.

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; }
}

Předpokládejme, že v databázi máme následující data:

Blog s BlogId = 1 a Name = 'ADO.NET Blog'
Blog s BlogId = 2 a name = 'The Visual Studio Blog'
Blog s BlogId = 3 a name = '.NET Framework Blog'
Author with AuthorId = 1 and Name = 'Joe Bloggs'
Reader with ReaderId = 1 and Name = 'John Doe'

Výstup spuštění kódu by byl následující:

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

Tyto příklady ilustrují několik bodů:

  • Metody Položky vrací položky pro entity ve všech stavech, včetně Odstraněno. Porovnejte tuto možnost s místním, která vylučuje odstraněné entity.
  • Položky pro všechny typy entit jsou vráceny při použití jiné než obecné položky metody. Pokud je použita obecná metoda položek jsou vráceny pouze pro entity, které jsou instancemi obecného typu. To bylo použito výše k získání položek pro všechny blogy. Použila se také k získání položek pro všechny entity, které implementují IPerson. To ukazuje, že obecný typ nemusí být skutečným typem entity.
  • LinQ to Objects lze použít k filtrování vrácených výsledků. Toto bylo použito výše k vyhledání entit libovolného typu, pokud jsou změněny.

Upozorňujeme, že instance DbEntityEntry vždy obsahují nenulovou entitu. Položky relací a položky zástupných procedur nejsou reprezentovány jako instance DbEntityEntry, takže není nutné je filtrovat.