Data binding issues - c#

I have started using the Entity Framework quite recently and think it is very good but I am a bit confused over a couple of things.
I'm writing a winforms-application where you are presented a list of persons in a list and if you click a particular person more information about that person appears in textboxes. Nothing fancy so far, but you are supposed to be able to edit the data about the person so data binding is nice here.
It works just fine but I am bit confused about what is the correct way to do it. First I did like this:
var query = _context.Person.Where(c => c.Id == chosenId);
this.personBindingSource.DataSource = query.ToList();
Then I read a bit and tried:
var local = _context.Person.Local;
IEnumerable<Customer> enumerable = local.Where(c => c.Id == chosenId);
this.personBindingSource.DataSource = enumerable.ToList();
This one seems to work fine as well.
Then I saw that someone suggested something like:
_context.Person.Load();
this.personBindingSource.DataSource = _context.Person.Local.ToBindingList();
I am a bit confused right now on what approach is the correct one and what is the difference between these three? Anyone can help me?

Depends on What You Want to do
I honestly never liked getting this answer because it seems to be the answer to everything but in this case it is really the only answer I can give
What the Local Property Does
Local gives you a reference to the current elements being tracked by the data context that hasn't been marked by delete, essentially you are asking for the current object that have already been loaded into memory by the context, you can read more about it here DbSet(TEntity).Local Property
What The Load Method Does
The Load method eagerly loads the targeted context, you can read more about it here.
DbExtensions.Load Method
What ToBindingList Does
Basically this is creating a two way binding between whatever entity you have created and the UI when you use a collection created using this method. That is that any changes in the UI should be automatically reflected in the related entities within this collection. You can read more about it using the following links
BindingList(T) Class DbExtensions.ToBindingList()
What Each Of Your Examples Do
First Example
var query = _context.Person.Where(c => c.Id == chosenId);
this.personBindingSource.DataSource = query.ToList();
Under the covers the following is going on
Creating a Query to be processed by the server with your linq expressionGetting the content from the database and creating a list around it
Here you are grabbing any of the people with Id of chosen Id from the database and then loading them into your application and creating a list
Second Example
var local = _context.Person.Local;
IEnumerable<Customer> enumerable = local.Where(c => c.Id == chosenId);
this.personBindingSource.DataSource = enumerable.ToList();
Under the covers the following is going on
Getting all of the currently tracked objects by the context object that have been hit by a query but have not been marked as deletedgetting all of the elements in local memory that have the Id chosen Id
Here you are grabbing any people that have already been loaded into the context this is not going to get all of your persisted data items, you must have hit them in other queries
Third Example
_context.Person.Load();
this.personBindingSource.DataSource = _context.Person.Local.ToBindingList();
Under the covers the following is going on
You are loading all of the people into local memoryYou create binding list (allows two way data binding between the objects and the UI) You bind the list to the UI element personBindingSource
Unless you want to load all of the items into memory this is probably not what you want to do, if the dataset ever grows large enough it will slow your program down and could possibly cause it to not work correctly (unlikely with case of person in most scenarios but is possible)
When You Should Use Them
FirstWhen you want to just get the data that matches your query into local memory and don't need a link between UI and the entitiesSecondWhen you have already run a query in the context and need to use it someplace else but don't need to rerun the query since it is currently in memoryThirdWhen you want to load all of the elements of an entity set into memory and create a two way databinding between them and a control

The only difference between the first and second example you have there is one is deferred and one is not. The third one is a little different as it creates a two way binding between your database and control datasource, effectively making the datacontext track changes to the list for you (add, & deletes).
In all your examples, so long as you keep you datacontext open changes to the objects themselves would be tracked.
As far as which way is correct, that depends on your application. Pick the approach that works best for you based on what you are trying to accomplish

Related

Should I re-utilize my EF query method and if yes, how to do it

I am using EF to get data from my MySQL database.
Now, I have two tables, the first is customers and project_codes.
The table project_codes have a FK to customers named id_customers. This way I am able to query which projects belong to a customer.
On my DAL, I got the following:
public List<customer> SelectAll()
{
using (ubmmsEntities db = new ubmmsEntities())
{
var result = db.customers
.Include(p => p.project_codes)
.OrderBy(c=>c.customer_name)
.ToList();
return result;
}
}
That outputs to me all my customer and their respective project_codes.
Recently I needed to only get the project codes, so I created a DAL to get all the project codes from my database. But then I got myself thinking: "Should I have done that? Wouldn't be best to use my SelectAll() method and from it use Linq to fetch me the list of project_codes off all customers?
So this that was my first question. I mean, re-utilizing methods as much as possible is a good thing from a maintainability perspective, right?
The second question would be, how to get all the project_codes to a List? Doing it directly is easy, but I failed to achieve that using the SelectAll() as a base.
It worked alright if I had the customer_id using:
ddlProject.DataSource = CustomerList.Where(x => x.id.Equals(customer_id))
.Select(p => p.project_codes).FirstOrDefault();
That outputed me the project codes of that customer, but I tried different approaches (foreach, where within whhere and some others at random) but they either the syntax fail or don't output me a list with all the project_codes. So that is another reason for me going with a specific method to get me the project codes.
Even if "common sense" or best practices says it is a bad idea to re-utilize the method as mentioned above, I would like some directions on how to achieve a list of project_codes using the return of SelectAll()... never know when it can come in hand.
Let me know your thoughts.
There's a trade-off here; you are either iterating a larger collection (and doing selects, etc) on an in-memory collection, or iterating a smaller collection but having to go to a database to do it.
You will need to profile your setup to determine which is faster, but its entirely possible that the in-memory approach will be better (though stale if your data could have changed!).
To get all the project_codes, you should just need:
List<customer> customers; //Fill from DAL
List<project_code> allProjects = customers.SelectMany(c => c.project_codes).ToList();
Note that I used SelectMany to flatten the hierarchy of collections, I don't think SelectAll is actually a LINQ method.

Not able to remove items from database when user deletes a row in data grid

When a user hits the button, I'm executing the following code.
using (Context context = new Context())
{
foreach (Thing thing ViewModel.Things)
context.Things.AddOrUpdate(thing);
context.SaveChanges();
}
The updates are executed except for when the user selected a row and hit delete button. Visually, that post is gone but it's not really being removed from the database because it's not in the view model anymore. Hence, the loppification only ticks for the remaining things and not touching the removees.
I can think of two ways to handle that. One really bad - to remove everything from the context, save it and then recreate based on the view model. It's an idiotic solution so I'm only mentioning it for the reference's sake.
The other is to store each removed post in an array. Then, when the user invokes the code above, I could additionally perform the deletion of the elements in that array. This solution requires me to build the logic for that and I'm having this sense that it should be done automagically for me, if I ask nicely.
Am I right in my expectation and if so, how should I do it? If not, is there a smarter way to achieve my goal than creating this kill squad array?
At the moment, I do a double loop, first adding and updating what's left in the data grid. Then, removing anything that isn't found there. It's going to be painful if the number of elements grows. Also, for some reason I couldn't use Where because I need to rely on Contains and EF didn't let me do that. Not sure why.
using (Context context = new Context())
{
foreach (Thing thing in ViewModel.Things)
context.Things.AddOrUpdate(driver);
foreach (Thing thing in context.Things)
if (!ViewModel.Things.Contains(thing))
context.Things.Remove(thing);
context.SaveChanges();
}
The first thing I want to advice you is you should use the AddOrUpdate extension method only for seeding migrations. The job of AddOrUpdate is to ensure that you don’t create duplicates when you seed data during development.
The best way to achieve what you need you can find it in this link.
First in your ViewModel class you should have an ObservableCollection property of type Thing:
public ObservableCollection<Thing> Things {get;set;}
Then in the ViewModel's constructor (or in another place), you should set the Things property this way:
context.Things.Load();
Things = context.Things.Local;
From the quoted link:
Load is a new extension method on IQueryable that will cause the
results of the query to be iterated, in EF this equates to
materializing the results as objects and adding them to the DbContext
in the Unchanged state
The Local property will give you an ObservableCollection<TEntity> that
contains all Unchanged, Modified and Added objects that are currently
tracked by the DbContext for the given DbSet. As new objects enter the
DbSet (through queries, DbSet.Add/Attach, etc.) they will appear in
the ObservableCollection. When an object is deleted from the DbSet it
will also be removed from the ObservableCollection. Adding or Removing
from the ObservableCollection will also perform the corresponding
Add/Remove on the DbSet. Because WPF natively supports binding to an
ObservableCollection there is no additional code required to have two
way data binding with full support for WPF sorting, filtering etc.
Now to save changes, the only you need to do is create a command in your ViewModel class that call SaveThingsChanges method:
private void SaveThingsChanges()
{
context.SaveChanges();
}

Entity Framework accessing db methods

I'm quite new at this so forgive me for not knowing how to word this properly.
I have setup Entity Framework with a rather large database and have been trying to learn how to manipulate tables in the database.
My first method worked but I ran into an error. It would load the data into a datagridview just fine but when sliding the bar over to view the tables not on the screen it would throw an error. This is the process that triggered the error:
using (var context = new MydbEntities())
{
var query = (from a in db.Configurations
select a);
var result = query.ToList();
dataGridView1.DataSource = result;
}
Now if I change the first line to MydbEntities db = new MydbEntities(); I don't get an error. I'm trying to follow online tutorials but I thought maybe someone could help me understand the difference in these two.
Basically you met lazy-load and fact that Context got disposed when you tried to query next batch of records.
You have serveral options here, best ones in my opinion are:
Use EntityDataSource. This way DataControl will take care of EF Context instantiation and disposal. (MSDN has pretty good specs on that).
Implement custom ObjectDataSource. Use EF context within ObjectDataSource methods, instantiating and disposing it when needed.
(This article on the topic is little outdated, but you still can get the idea).

Embedded RavenDb Query on Index

I am playing around with RavenDb I have a very simple class that contains a collection, I am trying to return all the objects where the contained collection has more than 1 record, cannot seem to make it work.
Note: I am using an In-Memory Embedded document store in LinqPad, reading some data from a RDBMS and inserting into the In-Memory store (this works, if I just Query<Agency>().Take(100) I see my records...
Any Idea's ?
below image just to show that the db does contain my data...
ok, I have figured it out, can't say I fully understand it...but...
PopulateRavenInMemory();
DatabaseCommands.PutIndex("MultipleAddresses",
new IndexDefinitionBuilder<Agency>
{
Map = agencies => from a in agencies
where a.Addresses.Count() > 1
select new {}
});
Query<Agency>("MultipleAddresses").Customize(x => x.WaitForNonStaleResultsAsOfNow()).Dump();
I understand the WaitForNonStaleResults call, that makes sense, but I don't really understand why my Map function cannot select the class, it seems to demand a projection, I can move on, but I hate not knowing why this is so.

sorting on related field in llblgen 2.6

I inherited an application that uses llblgen 2.6. I have a PersonAppointmentType entity that has a AppointmentType property (n:1 relation). Now I want to sort a collection of PersonAppointmentTypes on the name of the AppointmentType. I tried this so far in the Page_Load:
if (!Page.IsPostBack)
{
var p = new PrefetchPath(EntityType.PersonAppointmentTypeEntity);
p.Add(PersonAppointmentTypeEntity.PrefetchPathAppointmentType);
dsItems.PrefetchPathToUse = p;
// dsItems.SorterToUse = new SortExpression(new SortClause(PersonAppointmentTypeFields.StartDate, SortOperator.Ascending)); // This works
dsItems.SorterToUse = new SortExpression(new SortClause(AppointmentTypeFields.Name, SortOperator.Ascending));
}
I'm probably just not getting it.
EDIT:
Phil put me on the right track, this works:
if (!Page.IsPostBack)
{
dsItems.RelationsToUse = new RelationCollection(PersonAppointmentTypeEntity.Relations.AppointmentTypeEntityUsingAppointmentTypeId);
dsItems.SorterToUse = new SortExpression(new SortClause(AppointmentTypeFields.Name, SortOperator.Ascending));
}
You'll need to share more code if you want an exact solution. You didn't post the code where you actually fetch the entity (or collection). This may not seem relevant but it (probably) is, as I'm guessing you are making a common mistake that people make with prefetch paths when they are first trying to sort or filter on a related entity.
You have a prefetch path from PersonAppointmentType (PAT) to AppointType (AT). This basically tells the framework to fetch PATs as one query, then after that query completes, to fetch ATs based on the results of the PAT query. LLBLGen takes care of all of this for you, and wires the objects together once the queries have completed.
What you are trying to do is sort the first query by the entity you are fetching in the second query. If you think in SQL terms, you need a join from PAT=>AT in the first query. To achieve this, you need to add a relation (join) via a RelationPredicateBucket and pass that as part of your fetch call.
It may seem counter-intuitive at first, but relations and prefetch paths are completely unrelated (although you can use them together). You may not even need the prefetch path at all; It may be that you ONLY need the relation and sort clause added to your fetch code (depending on whether you actually want the AT Entity in your graph, vs. the ability to sort by its fields).
There is a very good explanation of Prefetch Paths and how they were here:
http://www.llblgening.com/archive/2009/10/prefetchpaths-in-depth/
Post the remainder of your fetch code and I may be able to give you a more exact answer.

Categories

Resources