How to use AutonumberAttribute.getNextNumber()? - c#

When I use the AutonumberAttribute.getNextNumber(), it gives me the next number of the sequence but it also make the next number to change.
IE if I call 2 time in a row:
nextNumber = AutoNumberAttribute.GetNextNumber(ARLetteringPiece.Cache, LetteringPiece, numbering, DateTime.Now);
first time i'll get "0000001"
second time i'll get "0000002"
I want to be able to know what the next number will be without modifying it's next value.
Is there a way to achieve this ?
Thanks a lot
Edit to answer the comments :
I have a custom table, my UI key is generated with Autonumbering, and I need to put this key in the lines of my other tables to "bind" them to my custom table. So I need to know what will be the autogenerated number.

It depends on the relationship between your DACs (tables).
You can solve this by using the PXDBChildIdentity in the fields of all the tables that need to store the new key.
For example, if your DAC's autonumber field is of type integer and is called MyDAC.MyAutonumberField.
You can add the attribute to all fields in your other DACs that need to store the value like this:
[PXDBInt()]
[PXDBChildIdentity(typeof(MyDAC.myAutonumberField))]
public virtual int? MyDACID { get; set; }
If the other DACs are "children" of your custom DAC you should use the PXParent attribute in all the child DACs on the field that references their parent like this:
[PXDBInt(IsKey = true)]
[PXDBDefault(typeof(MyDAC.myAutonumberField))]
[PXParent(typeof(Select<MyDAC,
Where<MyDAC.myAutonumberField,
Equal<Current<myAutonumberField>>>>))]
public virtual int? MyParentDacID { get; set; }

I managed to do it in another way : First I save my "header", then I update the lines with the value autogenerated for my header and then I save it again.
public static void createLettering(List<ARRegister> lines)
{
// We build a new LELettering piece
Lettrage graph = CreateInstance<Lettrage>();
LELettering piece = new LELettering();
piece.Status = ListStatus._OPEN;
piece.LetteringDateTime = DateTime.Now;
piece = graph.ARLetteringPiece.Insert(piece);
// We fill the checked lines with the autonumber of the piece
bool lineUpdated = false;
foreach (ARRegister line in lines)
{
if (line.Selected.Value)
{
if (!lineUpdated)
{
piece.BranchID = line.BranchID;
piece.AccountID = line.CustomerID;
piece = graph.ARLetteringPiece.Update(piece);
graph.Actions.PressSave();
}
line.GetExtension<ARRegisterLeExt>().LettrageCD = graph.ARLetteringPiece.Current.LetteringCD;
graph.ARlines.Update(line);
lineUpdated = true;
}
}
// If there are lines in our piece, we save it
// It saves our lettering piece and our modifications on the ARLines
if (lineUpdated)
{
graph.Actions.PressSave();
}
}

Related

Creating table at runtime

I'm new to databases and I'm not sure how to handle this situation. I have 3 tables connected this way:
Session <- 1:1 -> Document <- 1:1 -> DocumentData
So basically there is always 1 Session that has a Document which has a DocumentData.
I want to be able to add different types and columns of data to DocumentData, so for example I can have DocumentData with 3 columns of type DateTime,Int32,Int32. And then have another table with 5 columns of types Datetime,double,Int32,Int32,Int32. Basically what I'm going for is to have something like this in my code:
using(var unit = new UnitOfWork(new SessionContext()))
{
var data = unit.Sessions.GetCurrent().Document.DocumentData;
var row = data.Column[0].Rows[5]... etc.
}
This is because DocumentData is generated from csv specified by a user, so each DocumentData is made of different columns.
EDIT:
I want to know how to create a table on runtime and assign whatever columns I want to it. So I want to be able to do something like:
var doc = new Document();
session.Document = doc;
doc.Columns.Add(new Column() {Rows = rows});
doc.Columns.Add(new Column() {Rows = rows2});
doc.SaveChanges();
and then have second table with different columns.
EDIT2:
To make it more clear I want to convert this:
public class DocumentData {
public List<DocumentColumn> Columns { get; set; }
}
public class DocumentColumn {
public string ColumnName { get; set; }
public List<object> Rows { get; set; }
}
into ado.net entities so I can save them to database.
You can use a SQL statement to create tables at runtime (via dbcontext). I don't think that its possible to bind such a table to an entity / class at runtime after the database / context is initialized.
But if you don't have to use the different / variable columns as query / selection parameters, simply serialize the document class in a single BLOB column and your done.

asp:gridview with a list as datasource, which also has a dictionary

I have a table with closing codes, for example:
Code Description
CL1 Reason 1
CL2 Reason 2
AF1 After 1 period
AF2 After 2 periods
Also a table with orders. Orders will be imported once a week and the table has, besides the other columns, a column holding the batch date and one holding the closing code. This last one can be NULL when the order isn’t closed or has one of the codes from the first given table.
I want to have a gridview which list per batch the total number of orders, the number of closed orders and per reason the number of orders involved.
Already made a class like this:
public class BatchSales
{
public DateTime BatchDate { get; set; }
public int BatchCount { get; set; }
public int TotalClosed { get; set; }
public Dictionary<string, int> Counters { get; set; }
}
And a method which returns a List of BatchSales.
Defined an asp:gridview in my aspx-page, with AutoGenerateColumns=false. I like to get this filled with 5 columns (batch date, count, closed, CL1, CL2, AF1 and AF2) and per row the relevant data.
In my code behind already added the first 3. My problem is adding the columns based on the dictionary. I’ve the closing codes read in a list and doing this:
foreach (var item in reasons)
{
BoundField bf = new BoundField();
bf.HeaderText = item.Description;
bf.DataField = "xxxx";
bf.DataFormatString = "{0:n0}";
bf.HeaderStyle.VerticalAlign = VerticalAlign.Top;
bf.ItemStyle.VerticalAlign = VerticalAlign.Top;
bf.ItemStyle.Wrap = false;
gvBatch.Columns.Add(bf);
}
What do I need to define for xxxx to get the value from the dictionary? Or isn’t this the right approach?
What you're wanting is more of a pivot grid. That aside, you need either an item template in the dictionary column that shows your dictionary data as rows (not ideal) or what I would do in this case is just have a button in that column that said something like "Show Details" and either have a modal window pop up with the list of counts or send it to a details page with the list of counts.

Why don't these ElasticSearch fields appear in "Fields"?

I can't figure this one out. I'm creating a new ElasticSearch index using the ElasticProperty attributes/decorators. Here's how I create the index:
client = ClientFactory(indexName);
if (!client.IndexExists(indexName).Exists)
{
var set = new IndexSettings() { NumberOfShards = 1 };
var an = new CustomAnalyzer() { Tokenizer = "standard" };
an.Filter = new List<string>();
an.Filter.Add("standard");
an.Filter.Add("lowercase");
an.Filter.Add("stop");
an.Filter.Add("asciifolding");
set.Analysis.Analyzers.Add("nospecialchars", an);
client.CreateIndex(c => c
.Index(indexName)
.InitializeUsing(set)
.AddMapping<ItemSearchable>(m => m.MapFromAttributes())
);
}
And all of my fields are being created properly from the attributes specified on the class, except these two. They are C# enumerations, which is maybe part of the problem. I'm trying to save them in the index as numeric fields...
[Required, ElasticProperty(Index = FieldIndexOption.NotAnalyzed, Store = true, NumericType = NumberType.Short, IncludeInAll = false)]
public Enums.PlatformType PlatformType { get; set; }
[ElasticProperty(Index = FieldIndexOption.NotAnalyzed, Store = true, NumericType = NumberType.Short, OmitNorms = true, IncludeInAll = false)]
public Enums.ItemType ItemType { get; set; }
When I set up the index and check via Kibana, I don't see PlatformType or ItemType at all in the list of fields in the empty index.
When I insert a record, I can see the values in the source (JSON) as numbers (as expected), but the "Fields" are not there.
So I'm thinking it's either because they're C# enum type, or because I'm trying to store it as a number. But I'm stumped on why Elasticsearch is skipping these fields. Many thanks for any ideas you may have.
UPDATE 1 ... The search still works (even without those fields being shown in the Fields section). I'm thinking it might just be a Kibana problem. In the Table view, it shows my two fields like this...
and hovering over those triangle exclamation marks says "No cache mapping for this field. Refresh your mapping from the Settings > Indices page". But of course I can't find such a page within Kibana.
So I might be fine behind the scenes and this is a non-issue. Does anyone else have any insight on what might fix this, make it clearer, or whether this is known behaviour and I should just move on? Thanks.

Why do the members of my list get overwritten with the last member of said list?

I am trying to write a program that prints out (in a string variable) the following information about an mdb database:
Table Name
Total number of columns of the table
List of columns as follows:
Column Name:
Column Data Type:
To accomplish this I used two custom types (public classes) and of course, lists. Here is the code I have so far (which by the way has been adjusted not in small part thanks to questions and answers gathered here):
Here are the classes I created to define the two new types I am using:
public class ClmnInfo
{
public string strColumnName { get; set; }
public string strColumnType { get; set; }
}
public class TblInfo
{
public string strTableName { get; set; }
public int intColumnsQty { get; set; }
public List<ClmnInfo> ColumnList { get; set; }
}
Here is the code that actually gets the data. Keep in mind that I am using OleDB to connect to the actual data and everything works fine, except for the problem I will describe below.
As a sample, I am currently testing this code with a simple 1 table db, containing 12 columns of type string save for 1 int32 (Long Int in Access).
//Here I declare and Initialize all relevant variables and Lists
TblInfo CurrentTableInfo = new TblInfo();
ClmnInfo CurrentColumnInfo = new ClmnInfo();
List<TblInfo> AllTablesInfo = new List<TblInfo>();
//This loop iterates through each table obtained and imported previously in the program
int i = 0;
foreach (DataTable dt in dtImportedTables.Tables)
{
CurrentTableInfo.strTableName = Globals.tblSchemaTable.Rows[i][2].ToString(); //Gets the name of the current table
CurrentTableInfo.intColumnsQty = dt.Columns.Count; //Gets the total number of columns in the current table
CurrentTableInfo.ColumnList = new List<ClmnInfo>(); //Initializes the list which will house all of the columns
//This loop iterates through each column in the current table
foreach (DataColumn dc in dt.Columns)
{
CurrentColumnInfo.ColumnName = dc.ColumnName; // Gets the current column name
CurrentColumnInfo.ColumnType = dc.DataType.Name; // Gets the current column data type
CurrentTableInfo.ColumnList.Add(CurrentColumnInfo); // adds the information just obtained as a member of the columns list contained in CurrentColumnInfo
}
//BAD INSTRUCTION FOLLOWS:
AllTablesInfo.Add(CurrentTableInfo); //This SHOULD add The collection of column_names and column_types in a "master" list containing the table name, the number of columns, and the list of columns
}
I debugged the code and watched all variables. It works great (the table name and column quantity gets registered correctly, as well as the list of column_names, column_types for that table), but when the "bad" instruction gets executed, the contents of AllTablesInfo are not at all what they should be.
The table name is correct, as well as the number of columns, and the columns list even has 12 members as it should have, but each member of the list is the same, namely the LAST column of the database I am examining. Can anyone explain to me why CurrentTableInfo gets overwritten in this manner when it is added to the AllTablesInfo list?
You're creating a single TblInfo object, and then changing the properties on each iteration. Your list contains lots of references to the same object. Just move this line:
TblInfo CurrentTableInfo = new TblInfo();
to the inside of the first loop, and this line:
ClmnInfo CurrentColumnInfo = new ClmnInfo();
inside the nested foreach loop, so that you're creating new instances on each iteration.
Next:
Important
Make sure you understand why it was failing before. Read my article on references if you're not sure how objects and references (and value types) work in C#
Use camelCased names instead of CamelCased ones for local variables
Consider using an object initializer for the ClmnInfo
Change your type names to avoid unnecessary abbreviation (TableInfo, ColumnInfo)
Change your property names to avoid pseudo-Hungarian notation, and make them PascalCased
Consider rewriting the whole thing as a LINQ query (relatively advanced)
The pre-LINQ changes would leave your code looking something like this:
List<TableInfo> tables = new List<TableInfo>();
int i = 0;
foreach (DataTable dt in dtImportedTables.Tables)
{
TableInfo table = new TableInfo
{
Name = Globals.tblSchemaTable.Rows[i][2].ToString(),
// Do you really need this? Won't it be the same as Columns.Count?
ColumnCount = dt.Columns.Count,
Columns = new List<ColumnInfo>()
};
foreach (DataColumn dc in dt.Columns)
{
table.Columns.Add(new ColumnInfo {
Name = dc.ColumnName,
Type = dc.DataType.Name
});
}
tables.Add(table);
// I assume you meant to include this?
i++;
}
With LINQ:
List<TableInfo> tables =
dtImportedTables.Tables.Zip(Globals.tblSchemaTable.Rows.AsEnumerable(),
(table, schemaRow) => new TableInfo {
Name = schemaRow[2].ToString(),
// Again, only if you really need it
ColumnCount = table.Columns.Count,
Columns = table.Columns.Select(column => new ColumnInfo {
Name = column.ColumnName,
Type = column.DataType.Name
}).ToList()
}
}).ToList();
You have only created one instance of TblInfo.
It's because you only have a single instance of TblInfo, which you keep updating in your loop and then add another reference to it to the List. Thus your list has many references to the same object in memory.
Move the creation of the CurrentTableInfo instance inside the for loop.

Gantt Chart Predecessors using JSGrid control

I am trying to create a Gantt Chart generator, using the Share point control:
<Sharepoint:JsGrid>
I followed this tutorial: How to: Create a Gantt Chart Using the JS Grid Control
I also linked my Sharepoint TaskList as the data Source.
I developped a system of filters using some XML.
But I now want to manage predecessors and represent dependencies by an arrow.
To manage them, I used the last parameter of the EnableGantt function (ganttDependentsColumnName), which one just need the name of the column which contains the dependency information.
What I have to put in this column ?
What I tried is to fill it with the ID of the task, the lane of the DataRow containing predecessors, and I tried to put an object of the class Dependency :
class Dependency : IJsonSerializable
{
public object Key {get; set;} // RecordKey
public LinkType{get; set;} //LinkType
public string ToJson(Serializer s)
{
return JsonUtility.SerializeToJsonFromProperties(s,this);
}
}
(This code is from the answers in the tutorial)
In the Key, what do I have to put? If someone did it or know how to do it, It could be nice.
Not sure if you are still facing this issue. But this is what we did for the Predecessors column and this is as far as I understand this:
1] Change the Dependency class a bit to add a constructor as shown (otherwise it errors out).
2] Then, you basically need to pass a Dependency array to the Predecessors column which means that the JSGrid control needs to know the starting point/row and the ending point/row of the dependency (thus an array is required). JSON Serialization is taken care of already because of the inheritance and the ToJson methods so no worries there.
Code:
1] Dependency Class:
public class Dependency : IJsonSerializable
{
public object Key { get; set; } // recordKey
public LinkType Type { get; set; } // SP.JsGrid.LinkType
public Dependency() {
Key = DBNull.Value;
Type = LinkType.FinishStart;
}
public string ToJson(Serializer s)
{
return JsonUtility.SerializeToJsonFromProperties(s, this);
}
}
2] Add column if not targeting a SharePoint Task List (where data is an object of DataTable):
data.Columns.Add(new DataColumn("Predecessors",typeof(Dependency[])));
3] Set the right object array to the Predecessors column:
if (<<A predecessor exists>>){
Dependency[] dep = new Dependency[2];
dep[0] = new Dependency();
try{
/*
// Unique Identifier for your row based on what you are
// passing to the GridSerializer while initializing it
// (as a third parameter which is called keyColumnName)
// In my case I had to get it by doing some coding as
// shown. The first object in the array represents the
// previous row and so the unique identifier should
// point to the previous row
*/
dep[0].Key = (
data.Select(
"ID=" +
data.Rows[s]["PredecessorsID"].ToString()
)
[0]["Key"]
);
}catch (Exception ex){
dep[0].Key = DBNull.Value;
}
dep[0].Type = LinkType.FinishStart;
/*
// Unique Identifier for your row based on what you are
// passing to the GridSerializer while initializing it
// (as a third parameter which is called keyColumnName)
// In my case I had to get it by doing some coding as
// shown. The second object in the array represents the
// current row and so the unique identifier should
// point to the current row
*/
dep[1] = new Dependency();
try{
dep[1].Key = data.Rows[s]["Key"];
}catch (Exception ex){
dep[0].Key = DBNull.Value;
}
dep[1].Type = LinkType.StartFinish;
data.Rows[s]["Predecessors"] = dep;
}
Finally, pass the Predecessors column while calling the EnableGantt() function:
gds.EnableGantt(
Convert.ToDateTime(
dr["start Date"]
),
Convert.ToDateTime(
dr["Due Date"]
),
GanttUtilities.GetStyleInfo(),
"Predecessors"
);
Make sure that your StartFinish and FinishStart link types matches the correct rows and that your tasks are listed correctly with correct task start dates and task end dates and predecessor keys.

Categories