How to change items in cache - c#

Hello i want to change and alter values inside the cache of my acumatica cache i would like to know how to do it
for example i want to change the Ext. Cost value pro grammatically of the first line or the second line or can i check if there is already a "Data Backup" on transaction Descr.
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
if (Globalvar.GlobalBoolean == true)
{
PXCache cache = Base.Transactions.Cache;
APTran red = new APTran();
red.BranchID = Base.Transactions.Current.BranchID;
red.InventoryID = 10045;
var curyl = Convert.ToDecimal(Globalvar.Globalred);
red.CuryLineAmt = curyl * -1;
cache.Insert(red);
}
else
{
}
baseMethod();
}
this code add a new line on persist but if it save again it add the same line agaub u wabt ti check if there is already a inventoryID =10045; in the cache
thank you for your help

You can access your cache instance by using a view name or cache type. Ex: (Where 'Base' is the graph instance)
Base.Transactions.Cache
or
Base.Caches<APTran>().Cache
Using the cache instance you can loop the cached values using Cached, Inserted, Updated, or Deleted depending on which type of record you are looking for. You can also use GetStatus() on an object to find out if its inserted, updated, etc. Alternatively calling PXSelect will find the results in cache (PXSelectReadOnly will not).
So you could loop your results like so:
foreach (MyDac row in Base.Caches<MyDac>().Cache.Cached)
{
// logic
}
If you know the key values of the cache object you are looking for you can use Locate to find by key fields:
var row = (MyDac)Base.Transactions.Cache.Locate(new MyDac
{
MyKey1 = "",
MyKey2 = ""
// etc... must include each key field
});
As Mentioned before you can also just use a PXSelect statement to get the values.
Once you have the row to update the values you set the object properties and then call your cache Update(row) before the base persist and you are good to go. Similar if needing to Insert(row) or Delete(row).
So in your case you might end up with something like this in your persist:
foreach (APTran row in Base.Transactions.Cache.Cached)
{
if (Globalvar.GlobalBoolean != true || row.TranDesc == null || !row.TranDesc.Contains("Data Backup"))
{
continue;
}
//Found my row
var curyl = Convert.ToDecimal(Globalvar.Globalred);
row.CuryLineAmt = curyl * -1;
Base.Transactions.Update(row);
}

Related

How to update data in database using Entity Framework

I want to update data in a database using values from datagridview but I have not succeeded. My aim is to search through my datagrid view and if my product name exist in gridview, then I update the quantity.
if (bunifuDataGridView1.Rows.Count > 0)
{
foreach (DataGridViewRow row in bunifuDataGridView1.Rows)
{
if (Convert.ToString(row.Cells[2].Value) == bunifuTextBox11.Text)
{
row.Cells[5].Value = Convert.ToString(Convert.ToInt32(bunifuTextBox10.Text) + Convert.ToInt32(row.Cells[5].Value));
found = true;
obj5.ProductName = Convert.ToString(row.Cells[2].Value);
obj5.CostPricePerProduct = Convert.ToInt32(row.Cells[3].Value);
obj5.SellingPricePerProduct = Convert.ToInt32(row.Cells[4].Value);
obj5.Quantity = Convert.ToInt32(row.Cells[5].Value);
obj5.ExpiryDate = Convert.ToString(row.Cells[6].Value);
obj5.ProductNumber = Convert.ToInt32(obj2.ProductNumber);
obj5.Quantity = Convert.ToInt32(row.Cells[5].Value);
context.Entry.state = Entrystate.modified;
context.SaveChanges();
inboundgoods();
refreshcustomergrid();
}
}
if (!found)
{
inboundgoods();
}
}
else
{
inboundgoods();
}
I wish for my code to be able to search through datagridview for product name, and if there is a match, it should update that record by incrementing the stock quantity and save in stock database.
This is hard to debug without having the full app in front of us, but we can recommend some code changes that will assist with debugging:
if (bunifuDataGridView1.Rows.Count > 0)
{
foreach (DataGridViewRow row in bunifuDataGridView1.Rows)
{
// Compare the Product on each row, add a watch to this value to assist debugging
var product = Convert.ToString(row.Cells[2].Value);
if (product == bunifuTextBox11.Text) // consider rename bunfuTextBox11 to something meaningful, like 'ProductNameTextBox'
{
row.Cells[5].Value = Convert.ToString(Convert.ToInt32(bunifuTextBox10.Text) + Convert.ToInt32(row.Cells[5].Value)); // consider rename bunifuTextBox10 to something more meaningful like 'ProductQuantityTextBox'
found = true;
obj5.ProductName = Convert.ToString(row.Cells[2].Value);
obj5.CostPricePerProduct = Convert.ToInt32(row.Cells[3].Value);
obj5.SellingPricePerProduct = Convert.ToInt32(row.Cells[4].Value);
obj5.Quantity= Convert.ToInt32(row.Cells[5].Value);
obj5.ExpiraryDate = Convert.ToString(row.Cells[6].Value);
obj5.ProductNumber = Convert.ToInt32(obj2.ProductNumber);
obj5.Quantity = Convert.ToInt32(row.Cells[5].Value);
//context.Entry.state=Entrystate.modified;
// If your context has automatic change tracking enabled, this following line is not necessary
// But you need to make sure you are setting the State on the correct object tracker instance by passing it in to the Entry method.
var dbEntry = g.Entry(obj5);
if (dbEntry.State == EntryState.Detached)
dbEntry.State = EntryState.Added;
else
dbEntry.State = EntryState.Modified;
context.SaveChanges();
inboundgoods();
refreshcustomergrid();
}
}
if (!found)
{
inboundgoods();
}
}
else
{
inboundgoods();
}
If you are not getting to the found = true; line of code during debugging then review your comparison logic, look for spelling and whitespace issues, you may want to change the comparison to something like this if your inputs or stored data might have blank spaces or inconsistent letter casing.
if (product.Trim().Equals(bunifuTextBox11.Text.Trim(), StringComparison.OrdinalIgnoreCase))
Take the time to use meaningful names for your data entry field controls, it will make you code easier to read and understand, especially when you post code examples to forums like SO!

AspectGetter to get object from list

I have a list of items that I would like to bind to my ObjectListView and I think AspectGetter needs to be used to achieve this. How would I go about doing this?
I have tried this to generate additional columns but I am still unable to bind the data to show list items
int count = 0;
foreach (var disk in vmObject.DisksList)
{
// create column with vhd+count
OLVColumn diskColumn = new OLVColumn("Attached VHD " + count, disk.Path);
// this lets you handle the model object directly
diskColumn.AspectGetter = delegate(object rowObject)
{
// check if that is the expected model type
if (rowObject is Model.HyperVTools.VMInfo)
{
// return the value of disklist
return ((Model.HyperVTools.VMInfo)rowObject).DisksList;
}
else
{
return "";
}
};
columnsList.Add(diskColumn);
count++;
}
objectListView2.Columns.AddRange(columnsList.Cast<System.Windows.Forms.ColumnHeader>().ToArray());
objectListView2.AddObject(vmObject);
The foreach loop is unnecessary. You only need to create a column once. The same goes for the AspectGetter.
As soon as you add objects to the OLV, it will call the AspectGetter delegates to get the values automatically and create the corresponding rows. I suggest you take another look at the tutorial / examples.

cannot "Add or Update" Entity because Primery key cannot be changed

I am implementing an import routine, where a user pastes a specific formatted string into an input field, which in turn gets tranformated into an entity and then put into a database.
The algorithm checks if the entity already exists and either tries to update it or insert it into the database. Inserting works fine - updating fails.
//considered existing if Name and owning user match.
if (db.Captains.Any(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId))
{
var captainToUpdate = db.Captains.Where(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId).SingleOrDefault();
db.Entry(captainToUpdate).CurrentValues.SetValues(captain);
db.Entry(captainToUpdate).State = EntityState.Modified;
await db.SaveChangesAsync();
}
The problem at hand is, that written like this, it tries to update the primary key as well, (captain Id is 0, whereas captainToUpdate Id is already set) which results in an exception The property 'Id' is part of the object's key information and cannot be modified.
What do I need to change, so the enttiy gets updated properly. If it can be avoided I don't want to update every property by hand, because the table Captain contains 30ish columns.
What you can do is first set the Id of captain to be the same as the Id of captainToUpdate:
captain.Id = captainToUpdate.Id;
db.Entry(captainToUpdate).CurrentValues.SetValues(captain);
await db.SaveChangesAsync();
I would not use the entity Captain to transfer the data to the UI, but a DTO object that has all properties you want to copy and no more. You can copy values from any object. All matching properties will be copied, all other properties in captainToUpdate will not be affected.
Try something like this ?
var captainToUpdate = db.Captains.FirstOrDefault(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId);
if(captainToUpdate != null){//Update captain Here
captainToUpdate.Update(captain);
}else{//Create captain here
db.Captains.Add(captain);
}
db.Savechanges();
I had the same issue and solved it by extension method and reflection, of course it will be better to create standalone class with some cachning for relfection, but performance wasn't critical in my task.
public static class EnitityFrameworkHelper
{
public static void SetValuesByReflection(this DbPropertyValues propertyValues, object o, IEnumerable<string> properties = null)
{
var reflProperties = o.GetType().GetProperties();
var prop = properties ?? propertyValues.PropertyNames;
foreach (var p in prop)
{
var refp = reflProperties.First(x => x.Name == p);
var v= refp.GetValue(o);
propertyValues[p] = v;
}
}
}
and here is example how to use it
var entry = ctx.Entry(accSet);
entry.CurrentValues.SetValuesByReflection(eParameters, entry.CurrentValues.PropertyNames.Except(new [] { "ID"}));
Also be careful with foreign keys in object which you want to update, probably you want to exclude them too.

Checking to see if string exists in db using linq

Here's my attempt:
public void ReadLot(LotInformation lot)
{
try
{
using (var db = new DDataContext())
{
var lotNumDb = db.LotInformation.FirstOrDefault(r => r.lot_number.Equals(r.lot_number));
if (lotNumDb.lot_number != null && lotNumDb.lot_number.Length == 0)
{
Console.WriteLine("does not exist. yay");
var lotInfo = db.LotInformation.FirstOrDefault(r => r.lot_number.Equals(lotNumber));
}
else if (lotNumDb.lot_number.ToString().Equals(lot.lot_number))
{
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
}
Here what I want to do:
When the user uploads a file, I check if the deserialized string from memory is a duplicate in the database or not. If it is, pop up a dialog box saying it's a duplicate/already exists and have nothing happen afterward. If it is not a duplicate, proceed with application. Also, if the column in the table in the database is null, store the lot number there and proceed.
I noticed a few things. If the database is empty and I run the above, I get a null exception because I'm trying to find a lot number in db that is not there. How do I change the code above so that if I check in db and the column is null, then just add the number and not throw an exception when comparing. I think that might be the only problem right now.
I'm not sure what this is supposed to be doing, but you don't need it:
var lotNumDb =
db.LotInformation.FirstOrDefault(r => r.lot_number.Equals(r.lot_number));
Instead, just check for the existance of the lot_number passed to the method, and use Any to determine whether there were any matches. If it returns true, then the lot number is already in the database.
// Check for duplicates
var isDuplicate = db.LotInformation.Any(r => r.lot_number == lot.lot_number);
if (isDuplicate)
{
// Inform user that the lot_number already exists
return;
}
Console.WriteLine("does not exist. yay");
// Store the lot_number in the database
bool lotNumDbExists = db.LotInformation(r => r.lot_number.Equals(r.lot_number)).Any;
or .exists
This should return either a true or false of if it exists.

Cache only parts of an object

I'm trying to achieve a super-fast search, and decided to rely heavily on caching to achieve this. The order of events is as follows;
1) Cache what can be cached (from entire database, around 3000 items)
2) When a search is performed, pull the entire result set out of the cache
3) Filter that result set based on the search criteria. Give each search result a "relevance" score.
4) Send the filtered results down to the database via xml to get the bits that can't be cached (e.g. prices)
5) Display the final results
This is all working and going at lightning speed, but in order to achieve (3) I've given each result a "relevance" score. This is just a member integer on each search result object. I iterate through the entire result set and update this score accordingly, then order-by it at the end.
The problem I am having is that the "relevance" member is retaining this value from search to search. I assume this is because what I am updating is a reference to the search results in the cache, rather than a new object, so updating it also updates the cached version. What I'm looking for is a tidy solution to get around this. What I've come up with so far is either;
a) Clone the cache when i get it.
b) Create a seperate dictionary to store relevances in and match them up at the end
Am I missing a really obvious and clean solution or should i go down one of these routes? I'm using C# and .net.
Hopefully it should be obvious from the description what I'm getting at, here's some code anyway; this first one is the iteration through the cached results in order to do the filtering;
private List<QuickSearchResult> performFiltering(string keywords, string regions, List<QuickSearchResult> cachedSearchResults)
{
List<QuickSearchResult> filteredItems = new List<QuickSearchResult>();
string upperedKeywords = keywords.ToUpper();
string[] keywordsArray = upperedKeywords.Split(' ');
string[] regionsArray = regions.Split(',');
foreach (var item in cachedSearchResults)
{
//Check for keywords
if (keywordsArray != null)
{
if (!item.ContainsKeyword(upperedKeywords, keywordsArray))
continue;
}
//Check for regions
if (regionsArray != null)
{
if (!item.IsInRegion(regionsArray))
continue;
}
filteredItems.Add(item);
}
return filteredItems.OrderBy(t=> t.Relevance).Take(_maxSearchResults).ToList<QuickSearchResult>();
}
and here is an example of the "IsInRegion" method of the QuickSearchResult object;
public bool IsInRegion(string[] regions)
{
int relevanceScore = 0;
foreach (var region in regions)
{
int parsedRegion = 0;
if (int.TryParse(region, out parsedRegion))
{
foreach (var thisItemsRegion in this.Regions)
{
if (thisItemsRegion.ID == parsedRegion)
relevanceScore += 10;
}
}
}
Relevance += relevanceScore;
return relevanceScore > 0;
}
And basically if i search for "london" i get a score of "10" the first time, "20" the second time...
If you use the NetDataContractSerializer to serialize your objects in the cache, you could use a [DataMember] attribute to control what gets serialized and what doesn't. For instance, you could store your temporarary calculated relevance value in a field that is not serialized.

Categories