Collection Navigation property not Refreshed (reload) after Edit - c#

I use a function to load data for a datagrid, Each row in the grid show items from a collection navigation property, But when I edit the collection and reload data for datagrid the added or removed entries not showing it shows only old ones:
Here is a part of LoadData() function I use for refreshing datagrid data, I call this after editing collection (Note the collection is correctly saved to DB):
count = query.Count();
var resumes0 = query.Skip(args.Skip.Value).Take(args.Top.Value).ToList<Resume>();
resumes0.ForEach(r =>
{
context.Entry(r).Navigation("ResumeSkills").IsLoaded = false;
context.Entry(r).Collection(r => r.ResumeSkills).IsLoaded = false;
context.Entry(r).Collection(r => r.ResumeSkills).Load();
});
resumes = resumes0;

AsNoTracking() here:
var query = context.Resumes.Include(r => r.AcceptedResumes).Include(r => r.ResumeSkills).ThenInclude(rs => rs.Skill).AsNoTracking().AsQueryable();
solved my problem, But I need tracking entities. But it seems this is the only way to solve this problem.

Related

Reloading datagridview with a subset dataset C#

I have a GridView that is being populated by a subset .Select of Tables as below; then bound the list to the Grid View Data source.
However, I found myself calling LoadCandidates() many times! any time a record is changed or even when adding a new record, in order to refresh the Data Grid View. This means I have to query the database and then append it to the DGV Data Source every time a change happens, to maintain the below DGV headers (subset).
is there a better way of implementing this?
private async Task LoadCandidates()
{
var candiatesList = await _dbContext.Candidates
.Include(i => i.Candimmigration)
.Select(s => new
{
Id = s.Id,
FirstName = s.FirstName,
LastName = s.LastName,
DateOfBirth = s.DateOfBirth,
VisaNo = s.Candimmigration.VisaNo,
PassportNo = s.Candimmigration.PassportNo,
})
.ToListAsync();
dgv.DataSource = candiatesList;
}
UPDATE:
Managed to get the first part done, however, I am not sure how to only display the above fields in my gridview rather than showing the entire candidate properties
_dbContext.Candidates.Load();
dgvCandidates.DataSource = _dbContext.Candidates.Local.ToBindingList();
// only display the columns above (Id, FirstName, LastName, etc..)
I believe you might want to implement Reactive behavior to your GridView. This is achieved by Binding a UI element like your GridView to an Observable variable. Once you've made this binding, the grid will update itself every time the variable is set. There are many ways to do this and even frameworks that facilitate it.
Here is a good example of how to achieve this with regular .Net:
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-create-and-bind-to-an-observablecollection?view=netframeworkdesktop-4.8

Entity Framework edit data in grid bound to Entity which uses joins

I have a DataGridView which binds to a database through Entities (I am using a Database First approach).
I want to display data in a grid (and update this information before posting back to the database) for an Entity which contains Navigation Properties (i.e. Joins).
The context is that there are Permissions in the application; these permissions are separated into relevant sections and every user either has this permission or does not.
The relationships between the tables
In the dialog, there are two grids; one to show the sections available and another to show the permissions within that section and whether the user has that permission enabled; the bottom grid dynamically populates based on the row selected in the top grid.
The DataGridView has AutoGenerateColumns = false and this cannot change (this is because it's a custom DataGridView to which you add columns yourself due to additional properties being required); additionally, the DataPropertyName is set to that of the column I want to display from the appropriate table.
The two approaches I have explored so far are as follows:
Load entire USER_PERMISSIONS entity and bind the DataGridView:
PermissionsSectionContext.PERMISSIONS_SECTION
.Load();
PermissionsValues.USER_PERMISSIONS
.Where(x => x.USER_ID == 5)
.Load();
SectionsGrid.DataSource =
new BindingSource(
PermissionsSectionContext
.PERMISSIONS_SECTION.Local.ToBindingList(), null);
ValuesGrid.DataSource =
new BindingSource(
PermissionsValues
.USER_PERMISSIONS.Local
.Where(x => x.PERMISSIONS_NAME.PERMSECTION_ID == 1), null);
Problem: This loads the items fine and I can also edit the permissions values and post this back to the database however the Permissions names (i.e. x.PERMISSIONS_NAME.FRIENDLY_NAME) do not show in the grid:
The DataPropertyName values of the columns in the bottom grid are 'FRIENDLY_NAME' and 'VALUE'.
Creating Anonymous types and then setting the 'Column names' using these anonymous types:
PermissionsSectionContext.PERMISSIONS_SECTION
.Load();
PermissionsValues.USER_PERMISSIONS
.Where(x => x.USER_ID == 5)
.Load();
SectionsGrid.DataSource =
new BindingSource(
PermissionsSectionContext
.PERMISSIONS_SECTION.Local.ToBindingList(), null);
ValuesGrid.DataSource =
new BindingSource(
PermissionsValues
.USER_PERMISSIONS.Local
.Where(x => x.PERMISSIONS_NAME.PERMSECTION_ID == 1), null)
.Select(x => new {
FRIENDLY_NAME = x.PERMISSIONS_NAME.FRIENDLY_NAME,
VALUE = x.VALUE});
Problem: All the values show exactly as I want in the grids, including the Permission Name; however, you cannot edit the 'Value' column. Debugging this reveals that when the anonymous type is bound to the DataGridView, it is setting the ReadOnly property to true; this column still needs to be editable and for SaveChanges() to post these changes back, which does work for option (1).
Thanks to Gert Arnold's initial response about using named types (many thanks for the pointer!) I did a bit of reading around and I have managed to get this working as I require.
The first thing I did was create a class which contained only the fields that I was going to use as part of the data operations:
private class UserPermissionsValue
{
public int? PERMISSION_ID { get; set; }
public int? PERMSECTION_ID { get; set; }
public string FRIENDLY_NAME { get; set; }
public int? VALUE { get; set; }
}
As I needed to store all of the permissions which were applicable against the user and then only show, in the bottom grid, those which were relevant to the selected row in the top grid, I kept all of the permissions against the user in a List<UserPermissionsValue>:
private List<UserPermissionsValue> UserPermissionsList = new List<UserPermissionsValue>();
I could then get the records that I needed for this list using projection on the base entities:
UserPermissionsList = ValuesGrid.DbContext.USER_PERMISSIONS.Local
.Select(x => new UserPermissionsValue {
PERMISSION_ID = x.PERMISSION_ID,
PERMSECTION_ID = x.PERMISSIONS_NAME.PERMSECTION_ID,
FRIENDLY_NAME = x.PERMISSIONS_NAME.FRIENDLY_NAME,
VALUE = x.VALUE
}
).ToList();
Using the existing _RowEnter event I had set up, I could simply select the permissions from the section I needed and then bind this to the grid:
int PermissionsSection = ((DataEntity.PERMISSIONS_SECTION)ValuesGrid
.Rows[e.RowIndex].DataBoundItem).PERMSECTION_ID;
ValuesGrid.DataSource = UserPermissionsList
.Where(x => x.PERMSECTION_ID == PermSectionID)
.ToList();
This had the benefit of the modified values being maintained and 'remembered' when changing options in the top grid, as the value and subsequent changes were bound to UserPermissionsList.
Finally, when confirming the dialog, the actual entities were brought in and their values were updated to what they had been set to in the dialog itself; I decided it would be less of a load to bring back all entities in a single request and then iterate through the collection of entities locally, rather than get the records from the DB one-by-one and update them that way:
var PermissionsCommitList = ValuesGrid.DbContext
.USER_PERMISSIONS.Local;
foreach (DataEntity.USER_PERMISSIONS permission in PermissionsCommitList)
{
UserPermissionsValue ValueToCommit = UserPermissionsList
.Where(x => x.PERMISSION_ID == permission.PERMISSION_ID)
.First();
permission.VALUE = ValueToCommit.VALUE;
}
dtgPermissionsSettings.pFormGrid.DataEntityProvider.SaveChanges();

How to get list of modified objects in Entity Framework 5

I'm binding list of entities to a data grid view like this:
var orders = context.Order.ToList();
BindingList<Order> orderList = new BindingList<Order>(orders);
dataGridView1.DataSource = orderList;
User can edit or add new directly on datagridview. When user click Save button, in order to optimize performance, I want to retrieve list of entities that has been changed/new to perform insert/update. How can I achieve this?
EDIT Define add new row to gridview:
BindinList<Order> orders = (BindingList<Order>)dataGridView1.Datasource;
order.Add(new Order());
EDIT 2 Solve:
BindinList<Order> orders = (BindingList<Order>)dataGridView1.Datasource;
Order order = new Order();
context.Order.Add(order);
order.Add(order);
List<Object> modifiedOrAddedEntities = context.ChangeTracker.Entries()
.Where(x => x.State == System.Data.Entity.EntityState.Modified
|| x.State == System.Data.Entity.EntityState.Added)
.Select(x=>x.Entity).ToList();
When binding EF entities to a DataGridView it is often preferable to create an IBindingList from the DbSet.Local ObservableCollection. This way you get two way databinding and your new entities are automatically added to the context when adding via BindingSource.Add() or IBindingList.Add(). The simplest way to get this working, once properly bound, is to set DataGridView.AllowUserToAddRows to true and new rows the users enter will be new entities Added to the context.
context.Orders.Load();
BindingList<Order> bindingList = context.Orders.Local.ToBindingList();
BindingSource ordersBindingSource = new BindingSource();
ordersBindingSource.DataSource = bindingList;
dataGridView1.DataSource = ordersBindingSource ;
System.Data.Entity must be referenced to use .ToBindingList() and you must be using EF4.1 or greater.

LINQ and ComboBox DataSource issue

How far is the code for best functionallity?
I have two ComboBox, so the first is related for choose the company, and the second for choose the branch-office in relation with the one.
I note that the only way I can fill datasource with filtering .Where on LINQ is on this way, maybe Im wrong please take a moment for look the following snippet :
private void cboCompany_SelectedIndexChanged(object sender, EventArgs e)
{
var _index = ((ComboBox)sender).SelectedIndex;
using (DB db = new DB())
{
var su = (from s in db.Branchs select s);
if (cboCompany.SelectedIndex == 0)
{
cboBranch.DataSource = su.Where(x => x.codeCompany == 1).Select(x => x.name).ToList();
}
else if (cboCompany.SelectedIndex == 1)
{
cboBranch.DataSource = su.Where(x => x.codeCompany == 2).Select(x => x.name).ToList();
}
cboBranch.BindingContext = this.BindingContext;
cboBranch.DisplayMember = "name";
cboBranch.SelectedIndex = 0;
}
}
Thanks in Advance!
Rather than hand-coding this, I would make data binding do all this work for me. In particular, it can be set up thus:
Make it so that your Company class has a property to get all associated branches - e.g. Company.Branches. If you use LINQ to SQL or Entity Framework, there should be one there already.
Have two BindingSources, bsCompanies and bsBranches.
Set cboCompany.DataSource to bsCompanies, and cboBranch.DataSource to bsBranches
Set bsCompanies.DataSource to collection/DataSet that contains companies.
Set bsBranches.DataSource to Branches under bsCompanies (the form designer should let you do this after you do the previous step, if your collection is strongly typed).
Now whenever user picks a different company in the first combo, the current item in the companies binding source will change. This will cause binding for the second binding source to re-evaluate, and set list of branches for a newly selected company to be the source for the second combo.

Adding an item to a bound WPF ListBox

Ok, this has been a head scratcher for me. I have a ListBox I am binding to a linq query like so:
private IQueryable<Feed> _feeds;
public IQueryable<Feed> Feeds
{
get
{
if (_feeds == null)
{
var feedsQuery = from f in _db.Feed orderby f.Title select f;
_feeds = feedsQuery;
}
return _feeds;
}
}
public Options()
{
InitializeComponent();
this.DataContext = Feeds;
}
(For the record I've also tried List, instead of IQueryable)
Everything shows up great and I have a databound form that allows you to edit a record and all of those changes work just fine, the modified data shows up in the list.
The problem comes with I add an item. Nothing shows up in the list. The data goes into the database fine, but the only way to see the data is closing and restarting my app. I'm using the code below as an example:
Feed feed = new Feed()
{
ID = Guid.NewGuid(),
Url = "http://www.test.com",
Title = "Test"
};
_db.Feed.InsertOnSubmit(feed);
_db.SubmitChanges();
_db.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues);
(with or without the _db.Refresh nothing happens)
What's going on?
You are doing everything right, you jus need to use ObservableCollection. This will notify the ListBox about any changes in the list and refresh it automatically.
From MSDN
In many cases the data that you work
with is a collection of objects. For
example, a common scenario in data
binding is to use an ItemsControl
such as a ListBox, ListView, or
TreeView to display a collection of
records.
P.S. you don't need a db refresh
Unless notified otherwise, the ListBox only iterates once over its ItemsSource. Your query is only being run once.
The query object doesn't know when the database changes (and Refresh doesn't help; see below)--it's up to you to know (or anticipate) that and to rerun relevant queries at the appropriate times.
Stan R mentions ObservableCollection. That's fine, but simply storing the result of your query in an ObservableCollection won't solve the problem unless you do some work to update the collection yourself when the database changes. This means rerunning the query and manually adding new items and removing deleted items from the collection. (You could alternatively just rerun the query and set the entire result back in to the ListBox, but that means a whole new set of items will be created--not very performant, and maybe not what you want for other reasons.)
As an aside, your call to DataContext.Refresh is probably not doing what you think it is. From the docs:
This method is useful after an optimistic concurrency error to bring items into a state for another attempt. It updates the state of the primitive fields and properties on the objects.
Okay. I'm not positive this is 100% the correct way to use the ObservableCollection, but this seems to work:
private ObservableCollection<Feed> _feeds;
public ObservableCollection<Feed> Feeds
{
get
{
if (_feeds == null)
{
var feedsQuery = from f in _db.Feed orderby f.Title select f;
_feeds = new ObservableCollection<Feed>();
foreach (var item in feedsQuery)
{
_feeds.Add(item);
}
}
return _feeds;
}
}
And add my item:
Feed feed = new Feed()
{
ID = Guid.NewGuid(),
Url = "http://www.test.com",
Title = "Test"
};
_db.Feed.InsertOnSubmit(feed);
_db.SubmitChanges();
// manually update the list
Feeds.Add(feed);
It took me a little while to figure out I had to update the list manually (thanks Ben), but it all seems to work. Sorting would be nice, but I'll worry about that another time.

Categories

Resources