Why am I getting blank records when using PXDatabase.GetSlot to retrieve data from a table? - acumatica

I set up this IPrefetchable class because I needed to store a set of data in memory for quick access to it and wanted it returned as a dictionary. I'm using PXDatabase.GetSlot to save data from INItemLotSerial into a pxdatabase slot, however when dbRecords selects the records from it, the field values in it are null(lotserialnbr, inventoryID, etc) although it does return the correct count of records. This is my first time using PXDatabase.GetSlot so perhaps I'm missing something fairly simple.
Any suggestions would be greatly appreciated. Thank you.
class INItemLotSerialFetcher : IPrefetchable
{
private Dictionary<string, int?> _availlist = new Dictionary<string, int?>();
public void Prefetch()
{
// read database
var dbRecords = PXDatabase.Select<INItemLotSerial>();
//add results to Dictionary
foreach (var rec in dbRecords)
{
if (rec.LotSerialNbr != null)
{
_availlist.Add(rec.LotSerialNbr, rec.InventoryID);
}
}
}
public static Dictionary<string, int?> GetINList()
{
var def = GetSlot();
return def._availlist;
}
private static INItemLotSerialFetcher GetSlot()
{
return PXDatabase.GetSlot<INItemLotSerialFetcher>("INserialFetcherSlot", typeof(INItemLotSerial));
}
}

Try this change ala an example from Sergey Marenich
public static Dictionary<string, int?> GetINList()
{
return PXDatabase.GetSlot<INItemLotSerialFetcher>("INserialFetcherSlot", typeof(INItemLotSerial));
}

Related

Is there a way to iterate through the fields in a row of a PXResultSet?

Is it possible to use a foreach loop in a BLC to iterate through the fields of a PXResultSet to get the FieldNames?
Is this doable? I can't seem to find a good way.
Thanks...
The PXResultset records are selected from a view. You can get the field names from the View.
Here's a full example:
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
public override void Initialize()
{
// Get field list from data view
var dataView = new PXSelect<SOOrder>(Base);
string fieldNames = string.Join(",", GetFieldNames(dataView.View, Base.Caches));
// You don't need result set to get field names
PXResultset<SOOrder> resultSet = dataView.Select();
throw new PXException(fieldNames);
}
public string[] GetFieldNames(PXView view, PXCacheCollection caches)
{
var list = new List<string>();
var set = new HashSet<string>();
foreach (Type t in view.GetItemTypes())
{
if (list.Count == 0)
{
list.AddRange(caches[t].Fields);
set.AddRange(list);
}
else
{
foreach (string field in caches[t].Fields)
{
string s = String.Format("{0}__{1}", t.Name, field);
if (set.Add(s))
{
list.Add(s);
}
}
}
}
return list.ToArray();
}
}
When run, this example will show the fields names used in the data view in Sales Order screen SO301000 as an exception.
Field names are contained in Cache object. If you really need to get field names from PXResultset you need to iterate the cache types in the result set.
Example for first DacType (0) of result set:
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
public override void Initialize()
{
var dataView = new PXSelect<SOOrder>(Base);
PXResultset<SOOrder> resultSet = dataView.Select();
foreach (PXResult result in resultSet)
{
Type dacType = result.GetItemType(0);
foreach (var field in Base.Caches[dacType].Fields)
PXTrace.WriteInformation(field);
}
}
}

Does the Azure Storage Table query entities really has number limitations?

From MSDN, it seems there's a limitation for the number of entities returned by the Query service:
A query against the Table service may return a maximum of 1,000 entities at one time and may execute for a maximum of five seconds.
But as I wrote a sample to show this issue, I didn't find any limitations for the number of returned entities, here is my key code:
public class DataProvider
{
public static string PartitionKey
{
get { return "PartitionKey"; }
}
public static IEnumerable<CustomerEntity> MoreThanThousandData()
{
var result = new List<CustomerEntity>();
for (int i = 0; i < 1200; i++)
{
result.Add(new CustomerEntity(PartitionKey, Guid.NewGuid().ToString())
{
Name = Guid.NewGuid().ToString(),
Age = new Random().Next(10, 70)
});
}
return result;
}
}
Insert 1200 entities to the table:
public class AfterOptimize
{
public void InsertDataToTable()
{
var cloudData = DataProvider.MoreThanThousandData();
Console.WriteLine("Plan to insert {0} entities to the table.", cloudData.Count());
InsertDataToTableInternal(AzureTableService.Table, cloudData);
}
private void InsertDataToTableInternal(CloudTable table, IEnumerable<ITableEntity> data)
{
var splitedData = data.Chunk(100);
Parallel.ForEach(splitedData, item =>
{
var batchInsertOperation = new TableBatchOperation();
foreach (var tableEntity in item)
{
batchInsertOperation.Add(TableOperation.Insert(tableEntity));
}
table.ExecuteBatch(batchInsertOperation);
});
}
}
Then, read from the table, the partition key are all the same here:
public void ReadCloudData()
{
InsertMoreThanOneThousandDataToTable();
var query =
new TableQuery<CustomerEntity>().Where(TableQuery.GenerateFilterCondition("PartitionKey",
QueryComparisons.Equal, DataProvider.PartitionKey));
var result = AzureTableService.Table.ExecuteQuery(query);
Console.WriteLine("Read {0} entities from table.", result.Count()); // output 1200
}
I only used the latest Azure storage .NET client API.
I'm not able to find a documentation link but ExecuteQuery method handles continuation token internally and will return all entities in a table. Thus the behavior you're seeing is correct.
If you run Fiddler when you are executing this code, you will notice multiple requests are sent to table service. First request would be without continuation token but in subsequent requests you will see NextPartitionKey and NextRowKey querystring parameters.

Display a specific result using foreach in Dictionary C#

Problem that I'm unable to solve is that when I search a contact, if found than I need that contact to be the only output but it's displaying whole record. What should I do?
here is my code
namespace Telephone_Directory
{
class Program
{
public bool info()
{
Console.WriteLine("ENTER THE NAME TO BE SEARCHED");
Dictionary<string, uint> contact = new Dictionary<string, uint>();
contact.Add("usman", 03453648729);
contact.Add("Iqtiqa", 03159825052);
contact.Add("Aamir", 03343315412);
contact.Add("Ghous", 03323142783);
var items = from pair in contact
orderby pair.Value ascending
select pair;
string chk = Console.ReadLine();
if (contact.ContainsKey(chk))
{
foreach (KeyValuePair<string, uint> pair in contact)
{
Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
} return true;
}
else
return false;
}
static void Main(string[] args)
{
Program ob =new Program();
bool a=ob.info();
if (a == true)
{
Console.WriteLine("Your contact is found successfully");
}
else
Console.WriteLine("Not found");
Console.ReadLine();
}
}
}
You are checking if the Contact is available but you are iterating over the whole collection as an output. So you can completely remove the first query (var item = ...) because it does not have any effect because you are not using it anywhere.
I would propose accessing everything directly by the Dictionary Key like this
uint srchNumber;
contact.TryGetValue(chk, out srchNumber);
if (srchNumber != null)
Console.WriteLine("{0}: {1}", chk, srchNumber);
this will get you what you want.
But I would suggest to change the implementation because a Dictionary may be not optimal if you will have multiple items with the same name because it only allows you to have every key once. Instead I would suggest to use something like SortedList> or List<...>. Then you may query all possible results with a LINQ-Statement like
var results = from c in contact select c where c.key == chk
this will get you all Key-Value-Pairs belonging to a certain name.
But it is a matter of what you are trying to achieve.

Retrieving values of ReadOnly fields from DynamicData DetailsView in Edit Mode on Updating using LinqDataSource

I have several tables in my database that have read-only fields that get set on Inserting and Updating, namely: AddDate (DateTime), AddUserName (string), LastModDate (DateTime), LastModUserName (string).
All of the tables that have these values have been set to inherit from the following interface:
public interface IUserTrackTable
{
string AddUserName { get; set; }
DateTime AddDate { get; set; }
string LastModUserName { get; set; }
DateTime LastModDate { get; set; }
}
As such, I have the following method on the Edit.aspx page:
protected void DetailsDataSource_Updating(object sender, LinqDataSourceUpdateEventArgs e)
{
IUserTrackTable newObject = e.NewObject as IUserTrackTable;
if (newObject != null)
{
newObject.LastModUserName = User.Identity.Name;
newObject.LastModDate = DateTime.Now;
}
}
However, by the time it hits this method, the e.OriginalObject has already lost the values for all four fields, so a ChangeConflictException gets thrown during the actual Update. I have tried adding the four column names to the DetailsView1.DataKeyNames array in the Init event handler:
protected void Page_Init(object sender, EventArgs e)
{
// other things happen before this
var readOnlyColumns = table.Columns.Where(c => c.Attributes.SingleOrDefaultOfType<ReadOnlyAttribute>(ReadOnlyAttribute.Default).IsReadOnly).Select(c => c.Name);
DetailsView1.DataKeyNames = DetailsView1.DataKeyNames.Union<string>(readOnlyColumns).ToArray<string>();
DetailsView1.RowsGenerator = new CustomFieldGenerator(table, PageTemplates.Edit, false);
// other things happen after this
}
I've tried making that code only happen on PostBack, and still nothing. I'm at a lose for how to get the values for all of the columns to make the round-trip.
The only thing the CustomFieldGenerator is handling the ReadOnlyAttribute, following the details on C# Bits.
UPDATE: After further investigation, the values make the round trip to the DetailsView_ItemUpdating event. All of the values are present in the e.OldValues dictionary. However, they are lost by the time it gets to the LinqDataSource_Updating event.
Obviously, there are the "solutions" of making those columns not participate in Concurrency Checks or other ways that involve hard-coding, but the ideal solution would dynamically add the appropriate information where needed so that this stays as a Dynamic solution.
i Drovani, I assume you want data auditing (see Steve Sheldon's A Method to Handle Audit Fields in LINQ to SQL), I would do this in the model in EF4 you can do it like this:
partial void OnContextCreated()
{
// Register the handler for the SavingChanges event.
this.SavingChanges += new EventHandler(context_SavingChanges);
}
private static void context_SavingChanges(object sender, EventArgs e)
{
// handle auditing
AuditingHelperUtility.ProcessAuditFields(objects.GetObjectStateEntries(EntityState.Added));
AuditingHelperUtility.ProcessAuditFields(objects.GetObjectStateEntries(EntityState.Modified), InsertMode: false);
}
internal static class AuditingHelperUtility
{
internal static void ProcessAuditFields(IEnumerable<Object> list, bool InsertMode = true)
{
foreach (var item in list)
{
IAuditable entity = item as IAuditable;
if (entity != null)
{
if (InsertMode)
{
entity.InsertedBy = GetUserId();
entity.InsertedOn = DateTime.Now;
}
entity.UpdatedBy = GetUserId();
entity.UpdatedOn = DateTime.Now;
}
}
}
}
Sadly this is not possible with EF v1

Loading an object from a db4o database

I am developing an e-commerce website that utilises db4o as the backend. All was well until last week when I came across a problem that I have been unable to solve. The code below is quite straight forward. I open a database file, save an object and then try to retrieve it. However I get nothing back. The "users" variable has a count of zero.
public class Program
{
private static string _connectionString = string.Format(#"c:\aaarrrr.db4o");
static void Main(string[] args)
{
TestUser container = new TestUser() { id = 1, Name = "Mohammad", Surname = "Rafiq" };
Db4oFactory.Configure().Diagnostic().AddListener(new DiagnosticToConsole());
using (var dbc = Db4oFactory.OpenFile(_connectionString))
{
dbc.Store(container);
}
IList<TestUser> users = null;
using (var dbc = Db4oFactory.OpenFile(_connectionString))
{
users = dbc.Query<TestUser>(x => x.id == 1).ToList();
}
if (users.Count > 0)
{
Console.WriteLine("{0} {1} with id of {2}", users.First().Name, users.First().Surname, users.First().id);
}
else
{
Console.WriteLine("\nNo data returned.");
}
Console.ReadLine();
}
}
public class TestUser
{
[Indexed]
private int _id = 0;
private string _name = string.Empty;
private string _surname = string.Empty;
public int id { get { return _id; } set { _id = value; } }
public string Name { get { return _name; } set { _name = value; } }
public string Surname { get { return _surname; } set { _surname = value; } }
}
I have attached db4o diagnostic listener and I see nothing in the console output. Everything seems fine. I know I am writing to the file because I can see the file size increase and the timestamp is also updated. I have checked all the project settings and they are all set to default. I am using .net 4, visual studio 2010 beta and windows 7. I have done some reading regarding reflection permission but I cant see how this applies here. Any help or ideas would be knidly appreciated.
After calling store(), you need to commit() before leaving the using{} statement. You closed your database before committing your changes.

Resources