How can I pass a function with one parameter to an ICommand? - c#

Here's my ICommand:
public ICommand ConfirmLotSavedCommand {
get
{
return new RelayCommand(ConfirmLotSaved);
}
}
The problem is I have deserialized data that I want to store into database after a user clicks confirm button. If the user does not click on confirm or the lot number already exists, then I don't want to save the deserialized string in db.
I had trouble calling a function with one parameter inside my ConfirmLotSaved() method because of scope.
So I created a set the deserialized lot as a field and put the code to save to db inside of ConfirmLotSaved(). However, the field is null for some strange reason... I'm not sure why.
Here's my attempt:
private LotInformation lot; //field that is supposed to contain all the deserialized info
private void ConfirmLotSaved()
{
using (var db = new DDataContext())
{
bool lotNumDbExists = db.LotInformation.Any(r => r.lot_number == DeserialLotNumber);
if (lotNumDbExists == false)
{
successWindow.Message = "Successfully Saved Lot";
dialogService.ShowDialog(successWindow.Message, successWindow);
LotInformation newLot = new LotInformation();
if (newLot != null)
{
newLot.Id = lot.Id;
newLot.lot_number = lot.lot_number;
newLot.exp_date = lot.exp_date;
LotNumber = Lot.lot_number;
ExpirationDate = Lot.exp_date.ToString();
foreach (Components comp in lot.Components)
{
newLot.Components.Add(comp);
}
ComponentsList = newLot.Components;
foreach (Families fam in lot.Families)
{
newLot.Families.Add(fam);
}
FamiliesList = newLot.Families;
try
{
db.LotInformation.Add(newLot);
db.SaveChanges();
//Grabs the lot_number column from db that is distinct
var lotNum = db.LotInformation.GroupBy(i => i.lot_number).Select(group => group.FirstOrDefault());
//Loops through the lot numbers column in db and converts to list
foreach (var item in lotNum)
{
Console.WriteLine(item.lot_number);
}
LotNumList = lotNum.ToList();
Console.WriteLine("successfully");
}
catch
{
//TODO: Add a Dialog Here
}
}
else if (lotNumDbExists == true)
{
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
return;
}
}
}
}
Deserialization function to see where lot is grabbing data:
public void DeserializedStream(string filePath)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "lot_information";
xRoot.IsNullable = false;
// Create an instance of lotinformation class.
LotInformation lot = new LotInformation();
// Create an instance of stream writer.
TextReader txtReader = new StreamReader(filePath);
// Create and instance of XmlSerializer class.
XmlSerializer xmlSerializer = new XmlSerializer(typeof(LotInformation), xRoot);
// DeSerialize from the StreamReader
lot = (LotInformation)xmlSerializer.Deserialize(txtReader);
// Close the stream reader
txtReader.Close();
LotInformation newList = new LotInformation();
using (var db = new DDataContext())
{
bool isDuplicate = db.LotInformation.Any(r => r.lot_number == lot.lot_number);
if (newList != null && isDuplicate == false)
{
newList.Id = lot.Id;
newList.lot_number = lot.lot_number;
newList.exp_date = lot.exp_date;
DeserialLotNumber = newList.lot_number;
DeserialExpirationDate = newList.exp_date.ToString();
foreach (Component comp in lot.Components)
{
newList.Components.Add(comp);
}
DeserialComponentsList = newList.Components;
foreach (Families fam in lot.Families)
{
newList.Families.Add(fam);
}
DeserialFamiliesList = newList.Families;
}
else if (isDuplicate == true)
{
DeserialAnalytesList = null;
DeserialFamiliesList = null;
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
return;
}
}
}

I figured out what was wrong:
After setting private LotInformation lot; field before constructor, I redeclared locally my mistake:
LotInformation lot = new LotInformation();
Changed it to:
lot = new LotInformation();
and it works.

I suggest you to use RelayCommand's generic edition http://www.kellydun.com/wpf-relaycommand-with-parameter/
It will allow you to pass lot to your command from view, all you need to store lot in current DataContext.

Related

elastic search field update from csv file in c#

I am new to programming in C#
I want to read the csv file and update the fields in elastic search. I already have this function made by someone else and I want to call this function and update fields.
public static bool UpdateDocumentFields(dynamic updatedFields, IHit<ClientDocuments> metaData)
{
var settings = (SystemWideSettings)SystemSettings.Instance;
if (settings == null)
throw new SettingsNotFoundException("System settings have not been initialized.");
if (ElasticSearch.Connect(settings.ElasticAddressString(), null, out ElasticClient elasticClient, out string status))
{
return ElasticSearch.UpdateDocumentFields(updatedFields, metaData, elasticClient);
}
return false;
}
what I have done myself so far is :
private void btnToEs_Click(object sender, EventArgs e)
{
var manager = new StateOne.FileProcessing.DocumentManager.DocumentManager();
var path = "C:/Temp/MissingElasticSearchRecords.csv";
var lines = File.ReadLines(path);
foreach (string line in lines)
{
var item = ClientDocuments.GetClientDocumentFromFilename(line);
if (item != null)
{
var output = new List<ClientDocuments>();
manager.TryGetClientDocuments(item.AccountNumber.ToString(), 100, out output);
if (output!= null && output.Count <= 0)
{
var docs = new List<ClientDocuments>();
//var doc = docs.Add();
// var doc = docs.Add();
//docs.Add(doc);
foreach (var doc in output)
{
if (!item.FileExists)
{
//insert in elastic search
manager.UpdateDocumentFields(output, );
}
else
{
//ignore
}
}
My problem here is manager.updateDocumentFields() is a function. I am calling it but what I need to pass as a parameter? Thanks in advance.

More efficient way of Deserializing JSON string to 1 of 2 object types

I have data stored in a DB field acting as a cache from an API call.
This data is either of the type:
MyObject
List<MyObject>
I currently have the following code which uses NewtonSoft.JSON to take the string and (based on a successful deserialization) determine which of those types the string is.
var _lstObj = new List<MyObject>();
var _obj = new MyObject();
bool isList = false;
try
{
var n = JsonConvert.DeserializeObject<List<MyObject>>(cacheString);
if(n!=null && n.Count()>0)
{
_lstObj = n;
isList = true;
}
}
catch(Exception e)
{
isList = false;
}
if (!isList)
{
try
{
var x = JsonConvert.DeserializeObject<MyObject>(cacheString);
if (x != null && x.Data != null)
{
_obj = x;
}
}
catch(Exception e) { }
}
This feels kind of cumbersome...is there a better way to do this?
I'll always have a string for the incoming data.
It will only ever be 1 of those 2 types.
This should work:
var obj = (JToken)JsonConvert.DeserializeObject(cacheString);
if (obj.Type == JTokenType.Array)
{
var result = obj.ToObject<List<MyObject>>();
}
else
{
var result = obj.ToObject<MyObject>();
}
Checking first if the json string is a list should solve the problem.
if (cacheString.StartsWith("["))
{
var myList = JsonConvert.DeserializeObject<List<MyObject>>(cacheString);
}
else
{
var myObject = JsonConvert.DeserializeObject<MyObject>(cacheString);
}
I would use the expando object, that way you can keep adding properties if you like dynamically.
dynamic exp = new ExpandoObject();
try
{
exp.isList = true;
exp.value = JsonConvert.DeserializeObject<List<MyObject>>(cacheString);
}
catch (Exception e)
{
exp.isList = false;
exp.value = JsonConvert.DeserializeObject<MyObject>(cacheString);
}
but ideally I would cache the object as a List regardless if its one or many.

Object multiple reference IEntityTracker error while saving audit table

first I know this question has been asked but I really couldn't find an answer nor find the root of the problem so maybe a someone points me in the right direction.
I'm having the An entity object cannot be referenced by multiple instances of IEntityChangeTracker. error when trying to save into the log tables.
for the log table, I'm using
https://github.com/thepirat000/Audit.NET/tree/master/src/Audit.EntityFramework
so inside my DbContext class where I define the dbset, I have to override the onscopecreated function
the problem here is that when context.Savechanges run for the first audit record for each table it works but after first record, I get the multiple reference error.
so let's say I have the following tables
Languages table. with the following values
English,French,German
Countries Table with the following values
UK,France,Germany
for languages table, if I change English to English3 and save it works It records to the audit table but then for languages table, I can not do any changes at any records it's the same in every table
what am I missing?
private void SaveToLogTable(AuditScope auditScope)
{
foreach (var entry in ((AuditEventEntityFramework)auditScope.Event).EntityFrameworkEvent.Entries)
{
if(entry.Action is null) return;
if (TABLES.Any(x => x.T_TABLE_NAME.Equals(entry.Table)))
{
var newLog = new LOGS
{
LOG_ACTION = ACTIONS.FirstOrDefault(x => x.A_DESC == entry.Action)?.A_CODE,
LOG_DATE = DateTime.Now,
USERS = MyGlobalSettings.MyUser
};
if (entry.Changes != null)
{
foreach (var changes in entry.Changes)
{
var ch = new CHANGES
{
CH_COLUMN = changes.ColumnName,
CH_NEW_VALUE = changes.NewValue.ToString(),
CH_ORIGINAL_VALUE = changes.OriginalValue.ToString()
};
newLog.CHANGES.Add(ch);
}
}
if (entry.ColumnValues != null)
{
foreach (var kv in entry.ColumnValues)
{
var val = new VALUES
{
ColumnName = kv.Key,
ColumnValue = kv.Value.ToString()
};
newLog.VALUES.Add(val);
}
}
TABLES.First(x => x.T_TABLE_NAME.Equals(entry.Table)).LOGS.Add(newLog);
}
else
{
var table = new TABLES {T_TABLE_NAME = entry.Table};
var newLog = new LOGS
{
LOG_ACTION = ACTIONS.FirstOrDefault(x => x.A_DESC.Equals(entry.Action))?.A_CODE,
LOG_DATE = DateTime.Now,
LOG_USER_REFNO = MyGlobalSettings.MyUser.U_ROWID
//USERS = MyGlobalSettings.MyUser
};
if (entry.Changes != null)
{
foreach (var changes in entry.Changes)
{
var ch = new CHANGES
{
CH_COLUMN = changes.ColumnName,
CH_NEW_VALUE = changes.NewValue.ToString(),
CH_ORIGINAL_VALUE = changes.OriginalValue.ToString()
};
newLog.CHANGES.Add(ch);
}
}
if (entry.ColumnValues != null)
{
foreach (var kv in entry.ColumnValues)
{
var val = new VALUES
{
ColumnName = kv.Key,
ColumnValue = kv.Value is null? "": kv.Value.ToString()
};
newLog.VALUES.Add(val);
}
}
table.LOGS.Add(newLog);
//TABLES.Attach(table);
//TABLES.First(x => x.T_TABLE_NAME.Equals(entry.Table)).LOGS.Add(newLog);
TABLES.Add(table);
//TablesList.Add(table);
}
//entry.Entity
}
}

Getting double data in DB

I pull student data from 2 databases. 1 from an online SOAP API which can handle async calls and 1 from a local DB with an older services that doesnt support async.
I compare these databases and write the differences in a local sqlDB through EF.
Problem:
I get double entries in my EF DB. He puts the correct data and amount in arrays inside the method, but it looks like once he hits the db.savechanges() he jumps back up a few line and saves again.
I don't even know where this extra thread comes from.
Some code might be still there from numerous tries to solve it. For instance I tried with addrange but I get an error when he tries to add the FullVarianceList.
public async Task<bool> FullStudentCompare(string date) //format DD/MM/YYYY
{
try
{
//DB context
using (var db = new SchoolDbContext())
{
//GET DATA
//SMT (async)
List<SmtStudent> smtStdudentList = await GetAllSmartschoolStudents();
//Wisa (sync)
//on date, or if emty on current systemdate
List<WisaStudent> wisaList;
if (date == "")
{
wisaList = GetWisaStudentData(DateTime.Now.ToShortDateString());
}
else
{
wisaList = GetWisaStudentData(date);
}
//Flags and props needed for DB entry after compare
bool existsInLocalDb = false;
List<Variance> vList = new List<Variance>();
//Full list to add to DB outside foreach
List<Variance> fullVarianceList = new List<Variance>();
//Full List of new Students to write to DB outside foreach
List<DbStudent> fullStudentList = new List<DbStudent>();
//Compare lists
foreach (WisaStudent wstd in wisaList)
{
//determine correct classCode
string klasCode;
if (wstd.klasgroep.Trim() == "Klasgroep 00")
{
klasCode = wstd.klas.Trim();
}
else
{
klasCode = wstd.klasgroep.Trim();
}
//Create SmtStudent object for compare
SmtStudent tempStd = new SmtStudent(true,
wstd.voornaam.Trim(),
wstd.naam.Trim(),
wstd.stamboeknummer.Trim(),
wstd.geslacht.Trim(),
wstd.geboortedatum.Trim(),
wstd.straat.Trim(),
wstd.huisnummer.Trim(),
wstd.busnummer.Trim(),
wstd.postcode.Trim(),
wstd.gemeente.Trim(),
wstd.emailadres.Trim(),
wstd.GSM_nummer.Trim(),
wstd.levensbeschouwing.Trim(),
wstd.coaccountmoedervoornaam.Trim(),
wstd.coaccountmoedernaam.Trim(),
wstd.coaccountmoederemailadres.Trim(),
wstd.coaccountmoederGSM_nummer.Trim(),
wstd.coaccountvadervoornaam.Trim(),
wstd.coaccountvadernaam.Trim(),
wstd.coaccountvaderemailadres.Trim(),
wstd.coaccountvaderGSM_nummer.Trim(),
klasCode,
wstd.nationaliteit,
wstd.geboorteGemeente,
wstd.geboorteLand
);
//Find matching SmtStudent
SmtStudent smtStd = smtStdudentList.Find(i => i.Internnummer == wstd.stamboeknummer);
//Find matching Std in local DB
DbStudent dbStd = await db.Students.Where(i => i.Stamboeknummer == wstd.stamboeknummer).FirstOrDefaultAsync();
//if none exists in the local DB create an entity to update and write to DB
if (dbStd == null)
{
dbStd = new DbStudent(wstd.voornaam.Trim(),
wstd.naam.Trim(),
wstd.stamboeknummer.Trim(),
wstd.geslacht.Trim(),
wstd.geboortedatum.Trim(),
wstd.straat.Trim(),
wstd.huisnummer.Trim(),
wstd.busnummer.Trim(),
wstd.postcode.Trim(),
wstd.gemeente.Trim(),
wstd.emailadres.Trim(),
wstd.GSM_nummer.Trim(),
wstd.levensbeschouwing.Trim(),
wstd.coaccountmoedervoornaam.Trim(),
wstd.coaccountmoedernaam.Trim(),
wstd.coaccountmoederemailadres.Trim(),
wstd.coaccountmoederGSM_nummer.Trim(),
wstd.coaccountvadervoornaam.Trim(),
wstd.coaccountvadernaam.Trim(),
wstd.coaccountvaderemailadres.Trim(),
wstd.coaccountvaderGSM_nummer.Trim(),
klasCode,
wstd.loopbaanDatum,
wstd.nationaliteit,
wstd.geboorteGemeente,
wstd.geboorteLand
);
db.Students.Add(dbStd);
fullStudentList.Add(dbStd);
}
else
{
existsInLocalDb = true;
}
if (smtStd == null)
{
//Std doesn't exist in Smt -> New student
dbStd.IsNewStudent = true;
dbStd.ClassMovement = true;
//remove from wisaList
wisaList.Remove(wstd);
}
else
{
//clear vlist from previous iterations
vList.Clear();
//get all properties on the obj, cycle through them and find differences
PropertyInfo[] props = smtStd.GetType().GetProperties();
vList.AddRange(props.Select(f => new Variance
{
Property = f.Name,
ValueA = f.GetValue(smtStd),
ValueB = f.GetValue(tempStd),
Checked = false
})
.Where(v => !v.ValueA.Equals(v.ValueB) && v.ValueB != null)
.ToList());
//If the users allrdy exists in LocalDb delete all previously recorded variances
if (existsInLocalDb)
{
if (db.Variances.Where(j => j.Student.StudentId.Equals(dbStd.StudentId)).FirstOrDefault() != null)
{ //if the student allready exists we will recreate the variancelist, hence deleting all current items first
List<Variance> existingList = db.Variances.Where(j => j.Student.StudentId.Equals(dbStd.StudentId)).ToList();
foreach (Variance v in existingList)
{
db.Variances.Remove(v);
}
}
}
//Add new variances if vList is not empty
if (vList.Count > 0)
{
//Check if KlasCode is a variance -> set classmovement to true
if (vList.Where(i => i.Property == "KlasCode").FirstOrDefault() != null)
{
dbStd.ClassMovement = true;
}
else
{
dbStd.ClassMovement = false;
}
//add the StudentObject to the variance to link them 1-many
foreach (Variance v in vList)
{
v.Student = dbStd;
fullVarianceList.Add(v);
db.Variances.Add(v);
}
}
}
}
//add the full lists of variances and new students to DB
//db.Variances.AddRange(fullVarianceList);
//db.Students.AddRange(fullStudentList);
db.SaveChanges();
return true;
}
}
catch(Exception ex)
{
return false;
}
}
A couple of things:
It is important to understand that EF uses a unit of work pattern where none of the changes to the entities are persisted until SaveChanges is called which explains the "once he hits the db.Savechanges() he jumps back up" phenomenon.
When you have a 1 to many relationsship and you assign a collection of entities to a navigation property on another entity and then add that parent entity to the DbContext, EF marks those child entities to be added too. In your case dbStd is added at the line "db.Students.Add(dbStd);" and at the line "v.Student = dbStd;". This is most likely what is causing your duplicates.

Bing API Query Callback to Observable Collection

Created a Windows Store App using some info found on this post: How do I use the Bing Search API in Windows Phone?
Goal
Textbox - Type any term
Search Button - Searches that term and populates a GridView of pictures retrieved with the Bing API
Problem
I get the pictures, and they are received via my "OnQueryComplete" callback, but I can't figure out what the correct way to populate the collection would be. Since I can't figure out how to await this call, I (just to see if I could get it working, which it does) added a while loop (which you can probably see the issues with). What would be the correct way to do this? How do you handle callbacks for populating the GridView and having it wait until it's finished?
Current ViewModel Code
public bool itemsFinished = false;
private ObservableCollection<SearchResult> _ImageResults;
public ObservableCollection<SearchResult> ImageResults {
get {
if (_ImageResults == null) {
while (!itemsFinished) {
int i = 0;
i++;
}
}
return _ImageResults;
}
set {
_ImageResults = value;
}
}
public SearchResultViewModel() {
GetPictures("dogs");
}
public void GetPictures(string searchTerm) {
// This is the query - or you could get it from args.
string query = searchTerm;
// Create a Bing container.
string rootUri = "https://api.datamarket.azure.com/Bing/Search";
var bingContainer = new Bing.BingSearchContainer(new Uri(rootUri));
// Replace this value with your account key.
var accountKey = "myaccountkey";
// Configure bingContainer to use your credentials.
bingContainer.Credentials = new NetworkCredential(accountKey, accountKey);
// Build the query.
var imageQuery = bingContainer.Image(query, null, null, null, null, null, null);
imageQuery.BeginExecute(OnQueryComplete, imageQuery);
// var imageResults = imageQuery.Execute();
}
private void OnQueryComplete(IAsyncResult result) {
// ImageResults.Clear();
_ImageResults = new ObservableCollection<SearchResult>();
var query = (DataServiceQuery<ImageResult>)result.AsyncState;
var enumerableResults = query.EndExecute(result);
int i = 0;
foreach (var item in enumerableResults) {
SearchResult myResult = new SearchResult();
myResult.Title = item.Title;
myResult.ImageUri = new Uri(item.MediaUrl);
ImageResults.Add(myResult);
i++;
if (i >= 14) {
break;
}
}
itemsFinished = true;
}
Please excuse any syntax errors, I'm without a Visual Studio instance right now.
The problem I see is that you reset your ObservableCollection when you receive content.
Try it as follows:
private ObservableCollection<SearchResult> _ImageResults;
public ObservableCollection<SearchResult> ImageResults {
get
{
return _ImageResults;
}
set {
_ImageResults = value;
}
}
public SearchResultViewModel() {
_ImageResults = new ObservableCollection<SearchResult>(); // Just create it once.
GetPictures("dogs");
}
private void OnQueryComplete(IAsyncResult result) {
_ImageResults.Clear(); // Clear isn't bad, that way you keep your reference to your original collection!
//_ImageResults = new ObservableCollection<SearchResult>(); // We already have one. ObservableCollection works best if you keep on working with the collection you have.
var query = (DataServiceQuery<ImageResult>)result.AsyncState;
var enumerableResults = query.EndExecute(result);
int i = 0;
foreach (var item in enumerableResults) {
SearchResult myResult = new SearchResult();
myResult.Title = item.Title;
myResult.ImageUri = new Uri(item.MediaUrl);
ImageResults.Add(myResult);
i++;
if (i >= 14) {
break;
}
}
}
As far as I see (can't test sadly) this should work, provided you have bound your ObservableCollection the right way in your xaml.

Categories