MongoDB not querying - c#

Hi I am trying to query my Mongo Database by using a list of Facebook ID's as the parameter in order to return the list of users with corresponding accounts. The method works fine in the Unity Editor however when I run it on iOS I get a constructor error (I have set a blank default constructor in order to solve the issue however it still doesn't work)
Initial Method
public void FetchData()
{
//data = Mongo.Instance.players.FindAll().ToList();
if (FB.IsLoggedIn)
{
FB.API("me/friends", HttpMethod.GET, FriendsHighscoreHndlr);
}
}
Callback Method
public void FriendsHighscoreHndlr (IGraphResult FBresult){
var dict = Json.Deserialize(FBresult.ToString()) as Dictionary<string,object>;
var friendList = new List<object>();
friendList = (List<object>)(dict["data"]);
int _friendCount = friendList.Count;
Debug.Log("Found friends on FB, _friendCount ... " +_friendCount);
List<string> friendIDsFromFB = new List<string>();
for (int i=0; i<_friendCount; i++) {
string friendFBID = getDataValueForKey( (Dictionary<string,object>)(friendList[i]), "id");
string friendName = getDataValueForKey( (Dictionary<string,object>)(friendList[i]), "name");
Debug.Log( i +"/" +_friendCount +" " +friendFBID +" " +friendName);
friendIDsFromFB.Add(friendFBID);
}
//friendIDsFromFB.Add(AccessToken.CurrentAccessToken.UserId);
var query = Query.In("facebookID", BsonArray.Create(friendIDsFromFB));
//Debug.Log(query);
data = Mongo.Instance.players.Find(query).ToList();
}
Data Value for Key Method
private string getDataValueForKey(Dictionary<string, object> dict, string key) {
object objectForKey;
if (dict.TryGetValue(key, out objectForKey)) {
return (string)objectForKey;
} else {
return "";
}
}
Return Query Result
{
"_id" : ObjectId("XXXXXX"),
"facebookID" : "XXXXXXXXXXXXXX",
"name" : "John Doe",
"highScore" : 40501
}

Have you tried using a filter from Mongo's .NET drivers?
I would suggest trying something like the example below. (Note: this event is being fired on a pre-defined object which is being defined as a collection.)
var filter = Builders<Object>.Filter.Eq(obj => obj.attribute, List<IDs>);

Related

c# Async and await for loop with database

I'm facing a little problem. I've made a function that imports customers from my website to my database to create invoices and so.. When this function starts, it calls another function to import only new clients. Now I want to make this last function an await to prevent that my software at home can search for the newly imported customers. This customer import is pretty easy, it just selects only the customers which are not imported through a loop. But I also made some security's in there in case something goes wrong. You know, most errors come from human input errors... But what is the best way to make this function an async so that the other function can await while it's importing new customers?
public async Task<bool> ImportClients(bool onlyNewCustomers)
{
System.Data.DataTable Table = new System.Data.DataTable();
System.Data.DataTable CustomerTable = new System.Data.DataTable();
System.Data.DataTable KlantTable = new System.Data.DataTable();
int PrestaCustomerID = 0; //original customer id from the prestashop
int CustomerID = 0; //Original customerID from software customer
int CustomerIdInserted = 0; //id from the inserted customer id in the software
int Wait = 0; //This var is used for the mysql to wait a few miliseconds between x updates
string Sql = "";
string Prefix = "";
DateTime Bday;
//Vars for logging
int CustomersImported = 0;
StringBuilder NewCustomerInfo = new StringBuilder();
StringBuilder NewCustomerAddress = new StringBuilder();
//Select everything whithin the customers table. After that we look if the customer is imported else we update the clients credentials.
Sql =
"SELECT c.id_customer, id_gender, c.firstname, c.lastname, c.email, c.birthday, c.newsletter, c.optin, c.website, " +
"c.active, c.date_add, c.date_upd, c.imported FROM ps_customer c " +
(onlyNewCustomers ? "WHERE imported = 0 " : "") + "ORDER BY c.id_customer DESC;";
Table = Functions.SelectWebQuery(Sql);
if (Table.Rows.Count > 0)
{
for (int i = 0; i < Table.Rows.Count; i++)
{
if (somethingGoesWrong)
{
return false;
}
}
return await Task.WhenAll<bool>(true);
}
}
And what i've tried to call this function
public async static void OrderImport()
{
Functions fns = new Functions();
bool importCustomers = await fns.ImportClients(true);
}
I'm using .net 4.5 with mysql database in winforms.
Thanks!
Paul
My advice would be not to return a Task<bool>, but to return the newly imported customers. If there are no new customers, or there are minor errors, that are probably solved the next time that you import new customers, return an empty list. For errorst that need immediate action raise an exception.
FurtherMore I'd make an extra method to fetch new customers: FetchNewCustomers is easier to understand what it does than FetchCustomers(true).
public Task<ICollection<Customer> FetchNewCustomersAsync()
{
return FetchCustomersAsync(true);
}
public Task<ICollection<Customer> FetchCustomersAsync(bool newOnly)
{
... // TODO: implement
}
Apparently you have a method Functions.SelectWebQuery(string) that returns DataTable. This returned DataTable needs to be converted to a sequence of Customers.
As an extension method. See extension methods demystified
public static IEnumerable<Customer> ToCustomers(this DataTable table)
{
// in this method you handle the problems if the table is not a table of Customers
foreach(DataRow dataRow in table.AsEnumerable)
{
Customer customer = new Customer()
{
... // fetch values from DataRow: dataRow.Field<string>("Name");
}
yield return customer;
}
}
Usage:
string sql = ...
IEnumerable<Customer> customers = Functions.SelectWebQuery(sql).ToCustomers()
Or the async version:
IEnumerable<Customer> customers = (await Functions.SelectWebQueryAsync(sql))
.ToCustomers();
You need to select a sql, depending on whether you want all Customers or only the new Customers:
public string sqlTextAllCustomers {get; }
public string sqlTextNewCustomers {get; }
Now we are ready to implement FetchCustomersAsync:
public Task<ICollection<Customer> FetchCustomersAsync(bool newOnly)
{
string sqlText = newOnly ? sqlTextNewCustomer : sqlTextAllCustomers;
try
{
// in baby steps:
DataTable fetchedData = await Functions.SelectWebQuery(sqlText);
return fetchedData.ToCustomers();
}
catch(Exception exc)
{
// TODO: detect if serious error or not
if (isSeriousError)
throw exc;
else
{
// not a serious error: we'll process the new customers next time
return Enumerable.Empty<Customer>();
}
}
}

Importing a contact in Xamarin - emails return null

I am attempting to import a selected contact from the phone directory into my app. Using my code below, The contact name and phone number return successfully, but email always return null (the if (emailCursor.MoveToFirst()) clause always returns false). The ImportedContact is a simple class with 3 string properties, Name, Phone and Email. I've made sure my contact has an email address assigned.
static ImportedContact GetContactFromUri(Android.Net.Uri contactUri)
{
var importedContact = new ImportedContact();
try
{
string[] projection =
{
ContactsContract.Contacts.InterfaceConsts.Id,
ContactsContract.Contacts.InterfaceConsts.DisplayName,
ContactsContract.CommonDataKinds.Phone.Number
};
var cursor = CrossCurrentActivity.Current.Activity.ContentResolver.Query(contactUri, projection, null, null, null);
if (cursor.MoveToFirst())
{
importedContact.Name = cursor.GetString(cursor.GetColumnIndex(projection[1]));
importedContact.Phone = cursor.GetString(cursor.GetColumnIndex(projection[2]));
};
var id = cursor.GetString(cursor.GetColumnIndex(projection[0]));
var emailCursor = CrossCurrentActivity.Current.Activity.ContentResolver.Query(
ContactsContract.CommonDataKinds.Email.ContentUri,
null,
ContactsContract.CommonDataKinds.Email.InterfaceConsts.ContactId + " = " + id, null, null);
if (emailCursor.MoveToFirst())
{
int colId = emailCursor.GetColumnIndex(ContactsContract.CommonDataKinds.Email.InterfaceConsts.Data);
importedContact.Email = emailCursor.GetString(colId);
}
emailCursor.Close();
return importedContact;
}
catch
{
return null;
}
}
The actual issue was due to to the way I was calling the picker - I was incorrectly setting the intent type as specific to phone data:
public static void OpenContactPicker(Action<ImportedContact> callback)
{
_callback = callback;
Intent intent = new Intent(Intent.ActionPick);
intent.SetType(ContactsContract.CommonDataKinds.Phone.ContentType);
CurrentActivity.StartActivityForResult(intent, RequestCodes.ContactPicker);
}
and this was returning only the phone information and excluding the emails, website, etc. I changed this to:
public static void OpenContactPicker(Action<ImportedContact> callback)
{
_callback = callback;
Intent intent = new Intent(Intent.ActionPick, ContactsContract.Contacts.ContentUri);
CurrentActivity.StartActivityForResult(intent, RequestCodes.ContactPicker);
}
and this is returning all the contact information as required.

MultiSpeak API: How to Put Database Values in an Array

I am working on MultiSpeak API--I am not familiar with that. A function is as follows:
public meter[] GetMeterByAccountNumber(string accountNumber) {
meter myMeter = new meter();//IS this declaration right?
//some query work and next is sql data reader
int i = 0;
while (rdr.Read())
{
myMeter[i].deviceClass = rdr["deviceClass"].ToString();//error: Cannot apply indexing with [] to type 'meter'
i++;
}
return myMeter[]; //generates ERROR: Value expected.
}
I don't know what this return type of 'GetMeterByAccountNumber' is but it does expect a return of meter[] array.
GetMeterByAccountNumber is not the return type, it's the function name.
You could do somthing like this, however I would call it GetMetersByAccountNumber as it returns an array/IEnumerable
Also i'm not sure what deviceClass has to do with account number..
using System.Linq;
using System.Collections.Generic
public IEnumerable<meter> GetMetersByAccountNumber(string accountNumber) {
var items = new List<meter>();
//some query work and next is sql data reader
while (rdr.Read())
{
var deviceClass = rdr["deviceClass"].ToString();
var meter = new meter();
//Im guessing meter has some properties to set ?
meter.deviceClass = deviceClass;
items.Add(meter);
}
return items.AsReadOnly();
}
Here is what works for me, based on #Richard Friend's Answer. Thanks Richard!
public meter[] GetMetersByAccountNumber(string accountNumber)
{
meter[] final_return;
var items = new List<meter>();
while (rdr.Read())
{
var meter = new meter();
meter.deviceClass = rdr["deviceClass"].ToString();
items.Add(meter);
}
final_return = items.ToArray();
return final_return;
}

Ordered Dictionary in c# and unity3d FIREBASE

I currently have a firebase project that parses data from the web.
All the data is coming down however I'm wanting to reverse order the results.
I achieve this by doing the following code
public class ExampleItemModel{
public string name;
public string score;
public string level;
public int iconIdx1, iconIdx2, iconIdx3;
}
Then in my fetch data method :-
public void GetDataFromFireBase(string reference, Action<ExampleItemModel[]> onDone){
FirebaseDatabase.DefaultInstance
.GetReference(reference).OrderByChild("score")
.GetValueAsync().ContinueWith(task => {
if (task.IsFaulted) {
// Handle the error...
print("Error: " + task.Exception);
}
else if (task.IsCompleted) {
DataSnapshot snapshot = task.Result;
// Do something with snapshot...
var items = snapshot.Value as Dictionary<string, object>;
int count = items.Count;
var results = new ExampleItemModel[count];
int i = 0;
foreach(var item in items){
results[i] = new ExampleItemModel();
//print("items = " + item);
var values = item.Value as Dictionary<string, object>;
if (reference == "scores"){
print("values count =" + values.Count);
foreach(var value in values){
if(value.Key == "name"){ results[i].name = "" + value.Value;}
if(value.Key == "score"){ print("Score = " + value.Value); results[i].score = "" + value.Value;}
if(value.Key == "level"){ results[i].level = "" + value.Value;}
}
i++;
}
onDone(results);
}
});
}
Ive tried the following : -
in firebase rules I've set
".indexOn":"score",
"score": {
".indexOn": ".value"
}
I've also tried to add the following line of code
results = results.OrderByDescending( x => x.score);
onDone(results);
but I get the following error
severity: 'Error'
message: 'Cannot implicitly convert type 'System.Linq.IOrderedEnumerable' to 'ExampleItemModel[]' [Assembly-CSharp]'
at: '137,14'
source: ''
Would anybody please be able to help
thanks
Thanks Jeff for your response
On the back of this I managed to order the results by doing the following
List <ExampleItemModel> orderedArray = results.OrderByDescending(x => x.score).ToList();
Hope this will help somebody else with this issue in the future

C# and Reflection don't work twice in a row

I've a problem and I can't figured it out how to solve it.
I've a class for fetching data from a Database, in this class I've a method for a simple select * this method is called
List<T> All<T>(string tableName)
and you have to specify which resource you want to fetch, for example
All<User>("users")
And, aside from the classic SQL Reader and SQL Command, the core of the method is this
public override List<T> All<T>(string resource)
{
List<T> result = new List<T>();
using (MySqlConnection sqlConnection = new MySqlConnection(connectionString))
{
sqlConnection.Open();
try
{
string query = "SELECT * FROM " + resource + " WHERE 1=1";
using (MySqlCommand sqlCommand = new MySqlCommand(query, sqlConnection))
{
lock (locker)
{
MySqlDataReader reader = sqlCommand.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
T model = Activator.CreateInstance<T>();
Dictionary<string, object> _properties = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
string property = reader.GetName(i);
object value = reader.GetValue(i);
_properties.Add(property, value);
}
var type = model.GetType();
var method = type.GetMethod("SetProperties");
var invoked = method.Invoke(model, new object[] { _properties });
result.Add(model);
}
}
reader.Close();
}
}
}
catch (Exception ex)
{
Program.eventLogger.Add(new Event(EventType.Error, "SQL Data Providers", "Exception catched on All", ex));
}
finally
{
sqlConnection.Close();
}
}
return result;
}
Basically, based on the Type from the method header, the method will try to create an new instance of the specific type, later for each field from the query, it will fills all the attributes of the class on a temporaneous list. Once it's done it will try to call the method "SetProperties" which basically set every attributes of the class using reflection.
This is the core of SetProperties, equal for each entity:
public virtual bool SetProperties(Dictionary<string,object> properties)
{
if(this.ValidateData(properties))
{
FillNullableAttributes(properties);
// Iterate trough every key : value pairs in properties
foreach (KeyValuePair<string, object> kvp in properties)
{
if (this.data.Contains(kvp.Key))
{
var property = this.GetType().GetProperty(kvp.Key);
PropertyInfo propertyInfo = this.GetType().GetProperty(kvp.Key);
// Set the current fetched key with the given value if !null
if (kvp.Value != null)
{
Type fetchedType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
object safeConversion = (kvp.Value == null || kvp.Value == DBNull.Value) ? null : Convert.ChangeType(kvp.Value, fetchedType);
if (propertyInfo.CanWrite)
{
propertyInfo.SetValue(this, safeConversion, null);
}
}
}
}
return true;
}
return false;
}
In conclusion the result, which is a list, will be returned and the specific Entity will have its own BindingList filled. The binding list, for each entity is described as follow:
public static BindingList<Seller> items = new BindingList<Seller>();
This code works fine, even if there's a lot of space for improvements I know, but if I called it twice like this:
User.items = new BindingList<User>(provider.All<User>("users"));
User.items = new BindingList<User>(provider.All<User>("users"));
The second list will be filled by empty entities, the counting of the will be correct but they will be empties... and that shouldn't occurs.
The only thing that I figured it out, from the debugging, is that on the second call
var invoked = method.Invoke(model, new object[] { _properties });
invoked is set to false.
The result of:
var invoked = method.Invoke(model, new object[] { _properties });
Is the return value from your SetProperties method, not whether the method was invoked as your question indicates. Your SetProperties method is telling you that it was unable to do its work, debug that and you will find your answer.

Categories

Resources