EF Don't save using IQueryable in BindingSource - c#

I've created a code first context with a DbSet property
I work with Windows form. If I bind as follow:
_context.Schedules.Load();
scheduleBindingSource.DataSource = _context.Schedules.Local.ToBindingList();
All works great and when I save as follow:
this.Validate();
scheduleBindingSource.EndEdit();
_context.SaveChanges();
The data persists; But when I bind the data like this:
var res = _context.Schedules.Where(k => k.EmployeeName.Equals(employeeComboBox.Text)).ToList();
scheduleBindingSource.DataSource = res;
When I save data doesn't persis!
I'm thinking that the ToList() method is not good, but I can't find alternative to get a BindingList connected to the Local set of data inside the context.
Thanks,
Andrea

You can try this:
_context.Schedules.Where(k => k.EmployeeName.Equals(employeeComboBox.Text)).Load();
scheduleBindingSource.DataSource = _context.Schedules.Local.ToBindingList();
That should only bring the schedules that meet the condition. When you call the Load method after the Where method, it is going to bring to memory only the records that meet the condition. Later, when you call the Local property,it will give you an ObservableCollection<Schedule> that contains all the objects that are currently tracked by the DbContext which thy are going to be the elements you loaded before. At the end, when you call the ToBindingList extension method, it will returns an BindingList<Schedule> that stays in sync with the given ObservableCollection<Schedules>.

The reason that caused the non-persistance of the data is caused by DataGridView (or the BindingSource), that don't add to context the new istance of the just added row.
So I've disabled the AllowUserToAddRow property (now I'm using BindingNavigator Add Button)
And implemented these two events as follow:
private void scheduleBindingSource_AddingNew(object sender, AddingNewEventArgs e)
{
_scheduleAdding = true;
}
private void scheduleBindingSource_CurrentChanged(object sender, EventArgs e)
{
if (_scheduleAdding)
{
Schedule s = (Schedule)scheduleBindingSource.Current;
s.EmployeeName = employeeComboBox.Text;
s.From = new DateTime(dateTimePicker1.Value.Year, dateTimePicker1.Value.Month, 1);
_context.Schedules.Add(s);
_scheduleAdding = false;
}
}

Related

sorting in wpf using entity framework

I am testing the workings of WPF with Entity Framework. I have a SS table called Vendors {VendorCode, VendorName, Phone}.
I am sticking with only EF and I am able to display and navigate the recordset on a WPF form with buttons first, next, last etc. I used the instructions on the MSDN site (Create a simple data application with WPF and Entity Framework 6)
My problem is the recordset is sorted only in the order it was entered into SS. I would like to sort it by VendorCode or by VendorName to make it easier on the user. I can't seem to make it sort the recordset or table data coming through EF.
Can you please help? Thank you!
Here is a snippet of my code:
public Vendor newVendor { get; set; }
VendorsEntities context = new VendorsEntities();
CollectionViewSource VendorViewSource;
public MainWindow()
{
InitializeComponent();
newVendor = new Vendor();
VendorViewSource = ((CollectionViewSource)
(FindResource("VendorViewSource")));
DataContext = this;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// this next line doesn't do it
context.Vendors.OrderBy(Vendor => Vendor.VendorCode);
context.Vendors.Load();
VendorViewSource.Source = context.Vendors.Local;
}
private void NextCommandHandler(object sender, ExecutedRoutedEventArgs e)
{
VendorViewSource.View.MoveCurrentToNext();
}
You would need to set the result of OrderBy method in to some variable and then use that as OrderBy will return a new reference, or you can use the set the reference of context.Vendors to the reference returned by OrderBy() method.
Try doing it like:
var ordered = context.Vendors.OrderBy(Vendor => Vendor.VendorCode);
VendorViewSource.Source = ordered;
another way can be to order it after bringing the result back, but it is not a recommended approach, first approach should be preferred, but just giving another option which is also possible:
var vendors = context.Vendors.Load().OrderBy(Vendor => Vendor.VendorCode);
VendorViewSource.Source = vendors;
Hope it helps!
You are sorting context, not displayed items. Try:
VendorViewSource.Source = context.Vendors.OrderBy(Vendor => Vendor.VendorCode);

Querying database vs Querying IQueryable in order to create auto-complete functionality

I have a texbox control in my wpf application where I will like to get a auto complete listbox as the user types in. In other words I have something like google's search box:
I have managed to do that in two ways and I will like to know which one is more efficient.
First way:
As user types into the textbox every time the texbox changes I update the listbox by quering the database. therefore I have something like:
void textBox1_KeyUp(object sender, KeyEventArgs e)
{
// new text
var content = ((TextBox)sender).Text;
// I am selecting the posible items using ado.net
var posibleItems= PdvEntities.Entities.TableFoos.Where(TableFoo=> TableFoo.Description.Contains(content)).Select(c=>c);
listbox1.ItemsSource = posibleItems;
}
note that with this approach I will be querying the database every time a keyup event fires on that textbox.
Second way:
Instead of querying the database everytime the keyup event fires, I do the following:
// select all items and store that as a global variable
IQueryable allItems = PdvEntities.Entities.TableFoos.Select(a => a);
void textBox1_KeyUp(object sender, KeyEventArgs e)
{
// new text
var content = ((TextBox)sender).Text;
// I don't have the code but I will then filter variable
// allItems based if their description contains 'content'
// pseudo code
newFileter <- filter of allItems that contain content
listbox1.ItemsSource = newFileter;
}
not that in this case I query the database just once and everytime I need to add items to the listbox I will query a IQueryable variable instead of the database. I am afraid that if the database is to big this technique will consume a lot of memory.
Also I forgot to mention that the database may not be local. Right now I am connecting to the database locally but this application may run with a remote database connection. Which approach is more efficient?
There won't be any significant difference between the two versions. That's because the following code doesn't do what you think it does:
// select all items and store that as a global variable
IQueryable allItems = PdvEntities.Entities.TableFoos.Select(a => a);
It doesn't “store” all items in the field, it doesn't even go to the database at all. It's just a query that can retrieve all the items if you iterate it.
On the other hand, if you did something like
Foo[] allItems = PdvEntities.Entities.TableFoos.ToArray();
that would actually retrieve all items into memory. But there's no way of knowing which one will be more efficient, if we don't know everything about your database and your execution environment.
If your PdvEntities class is an EntityFramework Context then the following is a Linq-to-Entities query which will generate T-SQL against your database and get only your filtered items.
var posibleItems= PdvEntities.Entities.TableFoos
.Where(TableFoo=> TableFoo.Description.Contains(content)).Select(c=>c);
Not sure I understand your other solution. As #svik mentions You could use ToArray() and ToList() to get all you items in memory, but this isn't going to perfomant at all.
It looks like you need to throttle calls to your database, so as the use types, every n seconds you send a query with the filter.
Have a look at system.reactive. It will allow you throttle your keyup event in a nice way.
I wrote an article on this here:
http://www.gideondsouza.com/blog/implementing-simple-instant-search-with-rx-reactive-extensions (Which is about just throttling a search)
And then another one which talks about linq-to-entities to throttle a database search:
http://www.gideondsouza.com/blog/abstracting-reactive-extensions-for-sql-server-compact-and-implementing-an-instant-search
Based on the stuff I've written in my articles you could do something like this:
You'll need a little helper
public class ObservableHelper<T>
where T : class //or EntityObject
{
public ObservableHelper()
{
_dat = new PdvEntities();
}
PdvEntities _dat;
public IObservable<IList<T>> GetAllAsObservables
(Func<PdvEntities, IQueryable<T>> funcquery)
{
var getall = Observable.ToAsync<PdvEntities, IQueryable<T>>(funcquery);
return getall(_dat).Select(x => x.ToList());
}
}
Then in your form:
public Form1()
{
InitializeComponent();
//your playing with IQueryable<TableFoos>
_repo = new ObservableHelper<TableFoos>()
Observable.FromEventPattern(h => textBox1.KeyUp += h,
h => textBox1.KeyUp -= h)//tell Rx about our event
.Throttle(TimeSpan.FromMilliseconds(500), cs)///throttle
.ObserveOn(Scheduler.Dispatcher);//so we have no cross threading issues
.Do(a => SearchList(textBox1.Text))//do this method
.Subscribe();
}
IObservableHelper<TableFoos, PdvEntities> _repo;
void SearchList(string query)
{//AS MANY keystrokes are there, this function will be called only
// once every 500 milliseconds..
listBox1.Items.Clear();
listBox1.BeginUpdate();
var getfn = _repo.GetAllAsObservables
(d => d.TableFoos.Where(c => c.TableFoos.Contains(query)));
getfn.ObserveOn(this).Subscribe(resultList => //is an IList<TableFoos>
{
foreach (var item in resultList)
{
listBox1.Items.Add(...
}
listBox1.EndUpdate();
});
}

Create WPFon showdialog event

I am creating a new window popup window using
PopupWindows.PaymentsSummary paymentsSummary = new PopupWindows.PaymentsSummary
paymentsSummary.ParentWindow = Window.GetWindow(this);
paymentsSummary.ShowDialog();
on my load function in the Payment summary window I have
private void Window_Loaded(object sender, RoutedEventArgs e)
{
basepage.payments.BindPaymentSummaryToDataGrid(uiActiveItems, basepage.user.terminal.TerminalId, true);
basepage.payments.BindPaymentSummaryToDataGrid(uiInActiveItems, basepage.user.terminal.TerminalId, false);
}
The function is
public void BindPaymentSummaryToDataGrid(DataGrid dgrid, int terminalId, bool isActivePayment)
{
BLPinNumber pins = new BLPinNumber();
string pinNumber = String.Empty;
long pinId = pins.getPinId(terminalId, ref pinNumber);
using (var dbEntities = new DatabaseAccess.Schema.Entities())
{
dgrid.DataContext = dbEntities.getPaymentRecordsByPinId((int)pinId, isActivePayment);
}
}
The above code calls a Stored Proc in SQL Server and returns an object,
However when the app runs I get the error when clicking to show the popup on the following line paymentsSummary.ShowDialog();
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I have worked that down to the following code in the XAML for the datagrid
DataGrid ItemsSource="{Binding}" Grid.Column="{Binding}"
If i remove this code it works but the data doesnt load obvioulsy.
So what I believe I need to do is bind the datagrid onShowDialog method.
How do i create this ?
Or is there a better way of doing this using the Entity framework, im used to ASP.NET where working with DATAGRIDS seem easier, if ablight less powerful.
Many thanks
Your problem is lazy loading!, you got 2 options:
select the data with eager loading (change the getPaymentRecordsByPinId).
do not dispose the dbEntities while popup is open.

Update asp.net listview with LINQ, programmatically

I'm trying to find a good code sample to update a database entry in my listview control. I suppose I would need to extract the ID from somewhere (some label control?). I am using LINQtoSQL to talk with the database.
protected void lvTargets_ItemUpdating(object sender, ListViewUpdateEventArgs e)
{
InventoryDataContext inventory = new InventoryDataContext();
//Target target = from target in inventory.Targets
// where target.ID == lvTargets.Items[e.ItemIndex].FindControl("ID")
// *** Not sure how to go about this ^^^
//inventory.Targets.InsertOnSubmit(target);
//inventory.SubmitChanges();
lvTargets.EditIndex = -1;
BindInventory();
}
You can get the ID from the event arguments either like
e.Keys["ID"]
e.OldValues["ID"]
depending on your situation.

Commit Changes after DataGridView edit using LINQ2SQL (Winforms)

Given a DataGridView that has a BindingSource set like this:
On a winform, we add a BindingSource object using the designer, called myBindingScource.
Then on the Form.Designer.cs we add this to the InitializeComponents()
myBindingSource.DataSource = typeof(MyLinq.Person); //Mylinq is the autogenerated Linq Model/Diagram
Later, in the form itself we do:
myDataView.DataSource = myBindingSource;
and then we have a method that populates the Grid…
using ( myDataContext mdc = new MyDataContext() )
{
myDataView.DataSource = from per in mdc.person
select per;
}
As an aside note, I've set up the columns in Design Time, and everything shows ok.
Since the LINQ 2 SQL is not returning an Anonymous, the "myDataView" is editable, and here comes the question…
Question is: how do I persist those changes?
There are dozens of events in the datagrid, and I'm not sure which one is more appropriate. Even if I try one of the events, I still don't know what is the code I need to execute to send those changes back to the DB in order to persist the changes.
I remember back in the ADO.NET DataSet days, you would do dataadapter.Update(dataset);
Also imagine that both the retrieve and the persist() are on a Business Layer and the method signature looks like this:
public void LoadMyDataGrid(DataGridView grid);
that method takes the form's grid and populates it using the LINQ2SQL query shown above.
Now I'd like to create a method like this:
public void SaveMyDataGrid(DataGridView grid); // or similar
The idea is that this method is not on the same class (form), many examples tend to assume that everything is together.
RowValidated event would be a good place to check to see if it's time to persist changes to the database.
this.dataGridView1.RowValidated += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_RowValidated);
private void dataGridView1_RowValidated(object sender, DataGridViewCellEventArgs e)
{
MyLinq.Person person = dataGridView1.Rows[e.RowIndex].DataBoundItem as MyLinq.Person;
if (person != null)
{
// save this person back to data access layer
}
}
After your edit:
I wouldn't pass back a datagrid instance to your service layer. I'd pass back IEnumerable<MyLinq.Person> or IList<MyLinq.Person> then iterate over the collection in your service layer, and depending on the logic performed; persist the changes to the data access layer (your database)
The 'save' method on the DataContext object is SubmitChanges().
using (MyContext c = new MyContext())
{
var q = (from p in c.People
where p.Id == 1
select p).First();
q.FirstName = "Mark";
c.SubmitChanges();
}
As Michael G mentioned, you'll need to gather the changes, and pass them back to the bll object.

Categories