I have a Silverlight application that displays a map, and my intention is, when I mouse over a specific location in the map, the information about this location gets displayed somehow in the app. What I've done so far is to link the silverlight app to a webservice that retrieves this information for me, but now I'm stuck, and don't know how to proceed. I was following this tutorial., but when the tutorial wants to retrieve a list, I want to retrieve a single object. I was trying to use the datagrid, but I think it was not designed to perform what I want. I need some enlightment to tell me how to proceed.
Well ... I'll edit the code to show what problem I'm having. My code behind have this two methods:
private void MouseOverHarbor(object sender, RoutedEventArgs e)
{
Ellipse thisPath = (Ellipse)sender;
thisPath.Stroke = mySolidColorBrush;
DataRetrieverReference.Service1Client webService = new DataRetrieverReference.Service1Client();
webService.GetDataCompleted += new EventHandler<DataRetrieverReference.GetDataCompletedEventArgs>(webService_GetDataCompleted);
webService.GetDataAsync((int)thisPath.DataContext);
}
void webService_GetDataCompleted(object sender, DataRetrieverReference.GetDataCompletedEventArgs e)
{
NameField.Text = "Works";//No, it doesnt!
}
What I can see is that the event handler is never reached, but I don't know why. I just used the same code the tutorials taught, but I didn't achieve my goal yet. Am I missing something?
Remove the List< > part from List<Customer> in your calls and keep only the Customer part.
Change your queries from
var matchingCustomers = from cust in db.Customers
where cust.LastName.StartsWith(lastName)
select cust;
return matchingCustomers.ToList();
to
var matchingCustomers = from cust in db.Customers
where cust.LastName.StartsWith(lastName)
select cust;
return matchingCustomers.FirstOrDefault();
Related
I'd like to create an application that reads data from a database and then shows it through a UI. The user then can add/delete/update fields and save it to the DB, pretty standard, right?
I have two tables: Motors and Measures. Motors table has a lot of fields, one of them is "company". Of course, there can be several motors from the same company, so I would like to filter those companies and get only the distinct ones in a comboBox.
I'm still playing around with the language and VS, so I've made a simple version of the UI where the user can add a new motor, in fact, the user can add the company field, because I'm trying to add a new company and see if it updates automatically in the comboBox.
For this purpose, I'm using Entity Framework and this tutorial from msdn for data binding:
https://msdn.microsoft.com/en-us/data/jj682076.aspx
The problem is that when I add a new motor (with a new company), it doesn't update if I filter the distinct ones, I mean, the following code does work and automatically updates the comboBox with all the companies:
private void MainForm_Load(object sender, EventArgs e)
{
_context = new MotorsContext();
_context.Motors.Load();
this.companyBindingSource.DataSource = _context.companies.ToBindingList();
companyBindingSource.ListChanged += CompanyBindingSource_ListChanged;
}
And the following doesn't:
private void MainForm_Load(object sender, EventArgs e)
{
_context = new MotorsContext();
_context.Motors.Load();
this.companyBindingSource.DataSource = _context.Motors.Local.ToBindingList().Select(x => x.company).Distinct();
companyBindingSource.ListChanged += CompanyBindingSource_ListChanged;
}
I've created a ListChanged method to see when the software does detect that the list has, indeed, changed. In the first code it does trigger, but it doesn't in the second. Maybe the observer isn't detecting the change in the list when I add a filter?
private void CompanyBindingSource_ListChanged(object sender, ListChangedEventArgs e)
{
MessageBox.Show("List changed!");
}
And finally, the add motor button:
private void button1_Click_1(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(textBox1.Text))
{
Motor m = new Motor();
m.company = textBox1.Text;
_context.Motors.Add(m);
_context.SaveChanges();
MessageBox.Show($"New motor, id: {m.motorID}");
}
}
With the first implementation, the comboBox does update and shows every company (for every motor):
Push add button -> "List changed!" popup -> "New motor: id" popup
With the filter:
Push add button -> "New motor: id" popup
In fact the motor adds, but it doesn't show until the restart of the program.
Any idea will be much appreciated. I hope I've explained myself well.
The following line in the second example break the binding:
_context.Motors.Local.ToBindingList().Select(x => x.company).Distinct();
The reason is that the result of .Select(x => x.company).Distinct() is not a BindingList<Motor>, but a simple IEnumerable<string>
Use the following replacement:
var _companies = _context.Motors.Select(x => x.company).Distinct().ToList();
this.companyBindingSource.DataSource = _companies;
This line
this.companyBindingSource.DataSource = _context.Motors.Local.ToBindingList().Select(x => x.company).Distinct();
Returns IEnumerable<T>. In your case you want it to be list, so add .ToList();
this.companyBindingSource.DataSource = _context.Motors.Local.ToBindingList().Select(x => x.company).Distinct().ToList();
I am new to development and trying to retrieve a record to display and edit in a FormView on an ASP.NET web app page, using Entity Framework 5.0 (database first method), but I am not sure of the best way to go about this.
To retrieve the record, I use the following code:
protected void Page_Load(object sender, EventArgs e)
{
LoadData(int.Parse(Session["PersonID"].ToString()));
}
private void LoadData(int iPersonID)
{
using (PeopleEntities ctx = new PeopleEntities())
{
var query = (from a in ctx.People
where a.PersonID == iPersonID
select a).FirstOrDefault();
TextBoxFirstName.Text = query.FirstName;
TextBoxLastName.Text = query.LastName;
}
}
And to save it, I use:
protected void ButtonSave_Click(object sender, EventArgs e)
{
SaveEmployee(int.Parse(Session["PersonID"].ToString()));
}
private void SaveEmployee(int iPersonID = 0)
{
using (PeopleEntities ctx = new PeopleEntities())
{
var query = (from a in ctx.People
where a.PersonID == iPersonID
select a).FirstOrDefault();
query.FirstName = TextBoxFirstName.Text;
query.LastName = TextBoxLastName.Text;
ctx.SaveChanges();
}
}
It seems silly to me to have these two methods each query the database to retrieve and update the record, but, again, I am a novice, and maybe I'm just missing something. Is there a way to populate the controls on the FormView with the entity and have a method to save the record without having to manually assign the values (query.FirstName = TextBoxFirstName.Text, etc.) based on state?
I have seen the EntityDataSource, but I do not think that will be a good option for anything but the simplest of things.
Can anyone please tell me if what I am doing is ok or provide a better example or guidance?
Your help is greatly appreciated!
Best approach, IMHO, is that when you're retrieving data to DISPLAY only, do it without change-tracking. This will avoid performance issues. So, use the AsNoTracking method to avoid change-tracking proxies.
Than, for the update, you should load WITH change-tracking enabled, that's why there is no call to AsNoTracking on the save part.
Remember to check for null values. You're using the FirstOrDefault but since you're using the primary key, there won't be a second record, so just using SingleOrDefault. But since default (null) can happen, check for null values.
Also, use lambda expressions. They are not so easy to get with at first but you'll get used with minor effort and they will simplify your code a lot.
But from your question, there are some workarounds to avoid this but they are not the best approach. You should avoid long-living entities and prefer ViewModels for long-living objects, with the UnitOfWork pattern in mind for the repository and persistent entities.
If you really want that, you can Detach your entity from the context, use it everywhere and when you're ready, Attach it back and set it's state to Modified.
For this, take a look here: http://msdn.microsoft.com/en-us/library/bb896271.aspx
On your case, I'd suggest this:
private void LoadData(int iPersonID)
{
using (PeopleEntities ctx = new PeopleEntities())
{
// AsNoTracking will avoid performance hit of change-tracking here...
// Since we're building-up a view, not an update case yet, you don't have to create
// proxies that will check for entity changing...
var query = ctx.People.AsNoTracking().SingleOrDefault(_people => _people.PersonID == iPersonID)
// Rendering comes into action
if (query != null)
{
TextBoxFirstName.Text = query.FirstName;
TextBoxLastName.Text = query.LastName;
}
}
}
private void SaveEmployee(int iPersonID = 0)
{
using (PeopleEntities ctx = new PeopleEntities())
{
var query = ctx.Prople.SingleOrDefault(_person => _person.PersonID == iPersonID);
if (query != null)
{
query.FirstName = TextBoxFirstName.Text;
query.LastName = TextBoxLastName.Text;
ctx.SaveChanges();
}
}
}
This is also how I do it. It is necessary to retrieve the object you wish to update.
"It seems silly to me to have these two methods each query the database
to retrieve and update the record"
You are absolutely correct Don't Repeat Yourself should be a mantra principal that you should endeavour to follow.
Here you have chosen to retrieve the data in the page load event and retrieve it again in the button click event. Both these events are happening within the same instance of a WebPage. You could store it in an instance variable and re-use it in the button click, or you could set up a property for the entity that is "lazy loaded". There are all sorts of ways of doing it. Lazy loading is probably definitely overkill here because you will probably only use the property in the PageLoad should understand when it is necessary to go to the database and when it isn't.
It is necessary to go to the database to get the data you want to display when the page is first loaded. Thereafter the data is normally present in form values when the page posts back.
It is also necessary to go to the database when you are updating a record - which happens in this example when your user clicks on the save button.
Here is an example of lazy loading something i probably shouldn't have mentioned:
private People _Person;
//lazy loaded property
private People Person
{
get
{
if (_Person == null)
using (PeopleEntities ctx = new PeopleEntities())
_Person = GetPerson(ctx);
//returning a Person that isn't updateable because we've disposed of the context
return _Person;
}
}
//Retrieve an updateable person
private static object GetPerson(PeopleEntities ctx)
{
return (from a in ctx.People
where a.PersonID == int.Parse(Session["PersonID"]
select a).FirstOrDefault();
}
The other problem your code has is that you always set the TextBoxes in the PageLoad event from the values in the database. This means that when you get to the ButtonSave_Click event the values posted back have been overwritten by what was in the database and the changes won't be saved!.
So you should do this instead:
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)//Only do this first time it's loaded
{
TextBoxFirstName.Text = Person.FirstName;
TextBoxLastName.Text = Person.LastName;
}
}
And your button click can look like this:
protected void ButtonSave_Click(object sender, EventArgs e)
{
SavePerson(TextBoxFirstName.Text, TextBoxLastName.Text);
}
private SavePerson(string firstName, string lastName)
{
using (PeopleEntities ctx = new PeopleEntities())
{
var person = GetPerson(ctx);
person.FirstName = firstName;
person.LastName = lastName;
ctx.SaveChanges();
}
}
As you progress with your coding you will find that you want to repeat the SavePerson and GetPerson code on other pages. - and that is when you start introducing repositories or layers. Don't forget the mantra principal that you should endeavour to follow and move the code to another class so that you can re-use it.
That class should be in a PeopleRepository or some other layer. Eventually you will find that the code in the PeopleRepository looks very like the code in the MantraRepository and you will want to stop repeating yourself for different types.
That is when you should start using "generics". You replace the PeopleRepository and the MantraRepository with a Repository<People> and a Repository<Mantra> and the code is in one class defined something like public class BaseRepository<T>.
Before you go on that journey though, there is another thing about the Entity Framework bit - instead of
var query = (from a in ctx.People
where a.PersonID == iPersonID
select a).FirstOrDefault();
you should/could use
var query = ctx.People.Find(iPersonID)
From this source: Querying/Finding Entities
"The Find method on DbSet uses the primary key value to attempt to find
an entity tracked by the context. If the entity is not found in the
context then a query will be sent to the database to find the entity
there. Null is returned if the entity is not found in the context or
in the database.
Find is different from using a query in two significant ways:
A round-trip to the database will only be made if the entity with the
given key is not found in the context. Find will return entities that
are in the Added state. That is, Find will return entities that have
been added to the context but have not yet been saved to the database."
And now if you want to make that change and because you haven't repeated yourself anywhere you only have to change the code in the GetPerson method.
P.S. the code for getting a record will probably look like something like this when you finally implement that generic repository.
T e = Context.Set<T>().Find(id)
One line to get them all
try using
xxx.xxx.SelectSingleOrDefault(c => c.AccountSenderID == userId_int)
replaces the use of anonymous lambda expressions (using var for instance)
xxx.xxx.Select(c => new { c.FriendInvitationID,c.AccountSenderID,
c.Account1.AccountID, c.Account1.FirstName, c.Account1.LastName, c.Account1.Email,
c.FriendInvitationStatus, c.CreationDate })
.Where(c => c.AccountSenderID == userId_int).ToList();
you dont have to describe your object even though anonymous is more dynamic in that sense (image you want to retrieve a json object with two dfferent references of the same table, in that case you must declare fields because they will have same names, just a though)
I am currently working on Windows Store App in c#.
Now,
I am having a list box 'Listbox1' which gets its items on a button click event from a text box 'tasks', and have selected Items delete property on other button click event.
private void add_Click(object sender, RoutedEventArgs e)
{
string t;
t = tasks.Text;
if (t != "")
{
Listbox1.Items.Add(t);
}
else
{
var a = new MessageDialog("Please Enter the Task First");
a.Commands.Add(new UICommand("Ok"));
a.ShowAsync();
}
tasks.Text = "";
}
private void del_Click(object sender, RoutedEventArgs e)
{
for (int p = 0; p < Listbox1.SelectedItems.Count; p++)
{
Listbox1.Items.Remove(Listbox1.SelectedItems[p].ToString());
p--;
}
}
Now I want to save this list into local application storage, after user complete the changes (on a button click event perhaps).
And also to send all Listbox Items to another page(s).
I am not much a coder, I design things.
Please guide me by sample or reference.
Thank you in advance :)
If you have already stored the data to local storage, you could just read it in the OnNavigatedTo override of the other page. Otherwise, use the navigation parameter: http://social.msdn.microsoft.com/Forums/windowsapps/en-US/8cb42356-82bc-4d77-9bbc-ae186990cfd5/passing-parameters-during-navigation-in-windows-8
Edit: I am not sure whether you also need some information about local storage. This is easy: Windows.Storage.ApplicationData.Current.LocalSettings has a property called Values, which is a Dictionary you can write your settings to. Have a look at http://msdn.microsoft.com/en-us/library/windows/apps/hh700361.aspx
Edit: Try something like this code to store your list.
// Try to get the old stuff from local storage.
object oldData = null;
ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
bool isFound = settings.Values.TryGetValue("List", out oldData);
// Save a list to local storage. (You cannot store the list directly, because it is not
// serialisable, so we use the detours via an array.)
List<string> newData = new List<string>(new string[] { "test", "blah", "blubb" });
settings.Values["List"] = newData.ToArray();
// Test whether the saved list contains the expected data.
Debug.Assert(!isFound || Enumerable.SequenceEqual((string[]) oldData, newData));
Note, this is only demo code for testing - it does not make real sense...
Edit: One advice: Do not persist the list in your click handlers as this will become extremely slow as the list grows. I would load and save the list in the Navigation handlers, i.e. add something like
protected override void OnNavigatedTo(NavigationEventArgs e) {
base.OnNavigatedTo(e);
if (this.ListBox1.ItemsSource == null) {
object list;
if (ApplicationData.Current.LocalSettings.Values.TryGetValue("List", out list)) {
this.ListBox1.ItemsSource = new List<string>((string[]) list);
} else {
this.ListBox1.ItemsSource = new List<string>();
}
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e) {
if (this.ListBox1.ItemsSource != null) {
ApplicationData.Current.LocalSettings.Values["List"] = this.ListBox1.ItemsSource.ToArray();
}
base.OnNavigatedFrom(e);
}
Here is very nice simple example on SQLite DataBase Use in winRT app Development. Look at it and you will know how you can store your Data on the Local Machine. I learned Basic code from this example.
http://blogs.msdn.com/b/robertgreen/archive/2012/11/13/using-sqlite-in-windows-store-apps.aspx
Now, for ease of navigation let me suggest you a flow for this portion of your app.
take one ObservableCollection<> of string and store values of
that textBox into this ObservationCollection with onClick() and then
refer that ObservableCollection<String> to the ItemsList of the
listBox.
now at the time you need to send your Data to the next page, make one parameterised constructor of next page and pass that ObservableCollection<String> as it's parameter.
Now you can access those Data in your constructor and can use as however you want.
Hope this will help..
I'm trying to create a page where you get an overview of the sponsors of the project, the data is fetched from the database with the following service:
[OperationContract]
public IEnumerable<Sponsor> getSponsors()
{
var query = (from p in dc.Sponsors select p);
IEnumerable<Sponsor> i = query;
return i;
}
When I put my breakpoint on the i I can see that the data is correctly in there.
In my Sponsorspage I do the following
public partial class Sponsorspage : UserControl
{
IEnumerable<Sponsor> sponsors = null;
public Sponsorspage()
{
SponsorsServiceClient client = new SponsorsServiceClient();
client.getSponsorsCompleted +=new EventHandler<getSponsorsCompletedEventArgs>(client_getSponsorsCompleted);
client.getSponsorsAsync();
InitializeComponent();
}
void client_getSponsorsCompleted(object sender, getSponsorsCompletedEventArgs e)
{
if (e.Error != null)
MessageBox.Show(e.Error.ToString());
else
{
sponsors = e.Result;
foreach (Sponsor s in sponsors)
{
SponsorView control = new SponsorView(s.tekst);
SLWrapPanel.Children.Add(control);
}
}
}
For each sponsor in the database, I create the Sponsorview to which I give the source and text. You can see the code for my Sponsorview here.
public partial class SponsorView : UserControl
{
public SponsorView(string tekst)
{
txtSponsor.Text = tekst;
//Uri uri = new Uri(imageSource, UriKind.Relative);
//ImageSource imgSource = new BitmapImage(uri);
//imgSponsor.Source = imgSource;
InitializeComponent();
}
}
But when I run the page, I get the following error:
Object reference not set to an instance of an object.
at OndernemersAward.Views.SponsorView..ctor(String tekst)
at OndernemersAward.Views.Sponsorspage.client_getSponsorsCompleted(Object sender, getSponsorsCompletedEventArgs e)
at OndernemersAward.SponsorsServiceReference.SponsorsServiceClient.OngetSponsorsCompleted(Object state)
What I'm trying to do is give information (here string tekst) from the sponsor s to my user control, which it then uses to fill a textblock. Am I doing this wrong or?
Thanks! :)
Well, you're trying to iterate over results that you are supposed to hold in sponsors variable. However, please note that you're calling asynchronus version (and the only one available in Silverlight, as I recall) of getSponsors method. What it means is, you will not get results immediately after calling service method, but instead you need to wait until event with completed execution will be called.
I don't know why such thing could create some problems with debug, but it's definitely error in code that could result in problems with showing the page.
Here is very simple example on how you should retrieve result from service. Hope this will help you notice error in your approach.
I'm beginner with C# and wp7 platform and I have some problem with good idea to get request from web service.
I made webservice in PHP (nusoap - WSDL) and everything is working fine in "normal" using.
Now I have ObservableCollection saved in IsolatedStorage with I load when Page is open (List of watched stacks exchange). Then I want to refresh data for every item from web service.
I don't know whether this is a good idea.
Code:
private GPWWebservicePortTypeClient client = new GPWWebservicePortTypeClient();
private ObservableCollection<WebServiceClass.ItemGetValues> StoredStock =
new ObservableCollection<WebServiceClass.ItemGetValues>();
public const string _fileName = "listaObserwowanych.xml";
public Page()
{
InitializeComponent();
DataContext = App.ViewModel;
this.Loaded += new RoutedEventHandler(Page_Loaded);
client.GetLastValueCompleted +=
new EventHandler<GetLastValueCompletedEventArgs>(client_GetLastValueCompleted);
foreach (var itemGetValuese in App.ViewModel.Items)
{
client.GetLastValueAsync(itemGetValuese.name);
}
var o =
Observable.FromEvent<GetLastValueCompletedEventArgs(client,"GetLastValueCompleted")
.Subscribe(setList);
}
void client_GetLastValueCompleted(object sender, GetLastValueCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(Convert.ToString(e.Error));
}
else
{
ObservableCollection<WebServiceClass.ItemGetValues> ListValues =
(ObservableCollection<WebServiceClass.ItemGetValues>)
JsonConvert.DeserializeObject(e.Result,
typeof(ObservableCollection<WebServiceClass.ItemGetValues>));
StoredStock.Add(ListValues[0]);
}
}
private void setList(IEvent<GetLastValueCompletedEventArgs> ex)
{
List.ItemsSource = StoredStock;
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
App.ViewModel.LoadData();
List.ItemsSource = App.ViewModel.Items;
}
Like u see I use RX to call method client_GetLastValueCompleted add store result to auxiliary variable (StoredStock). Then refresh List in setList method, but that method is client_GetLastValueCompleted what is not soo good idea, becouse I need to run that method only when all of runned GetLastValueAsync in foreach is completed.
Second problem: becouse of async web service method StoredStock sometime have different order than App.ViewModel.Items .
Any good idea how to do that in right way?
Best regards,
Lukas
You're really mixing up a number of ways to call web services and Rx. You really need to decide on a single way and stick to it.
If you're going to use Rx, then you'll have something like this:
public Page()
{
InitializeComponent();
DataContext = App.ViewModel;
this.Loaded += new RoutedEventHandler(Page_Loaded);
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
App.ViewModel.LoadData();
var storedStock =
new ObservableCollection<WebServiceClass.ItemGetValues>();
List.ItemsSource = storedStock;
var values =
Observable.Using<WebServiceClass.ItemGetValues, GPWWebservicePortTypeClient>
(() => new GPWWebservicePortTypeClient(), ws =>
{
var clientGetLastValue = Observable
.FromAsyncPattern<string, GetLastValueResponse>
(ws.BeginGetLastValue, ws.EndGetLastValue);
Func<string, WebServiceClass.ItemGetValues> deserializeFirst = r =>
((List<WebServiceClass.ItemGetValues>)JsonConvert
.DeserializeObject(r,
typeof(List<WebServiceClass.ItemGetValues>)))
.First();
return
from item in App.ViewModel.Items
from e in clientGetLastValue(item)
select deserializeFirst(e.Result);
});
values.Subscribe(storedStock.Add);
}
You'll have to get the right method call names for your web service client, but the code should roughly be right. Let me know how you go.
I corrected the code above. Should have returned the query inside the Using call rather than assign it to values.
I corrected the call to FromAsyncPattern to use the correct method names and return type from the actual web service reference class sent via email.
It should look like this:
Observable.FromAsyncPattern<string, GetLastValueResponse>
(ws.BeginGetLastValue, ws.EndGetLastValue);
If you're a beginner with C#, try to avoid RX for the time being. It is a cool technology, but if you use it without clear understanding of what is going on, it will bring more problems than solve.
Use a simple event, and when each async item arrives, locate and update the correspondent one in the stored list.