AX CRUD
[ClassInitialize()]
public static void MyClassInitialize(TestContext testContext)
{
// populate proxy pool
ppt.Start();
// some set up for global variables
// Yes, I know this is in this class twice. But it is needed in the constructor for the materialization test in CRM which calls into this class.
context.Company = axTestCompany;
productClass = GetProductClassReference();
prodClassNameForTest = productClass.Name;
//create many products and add to a static array for Read. This avoids a SQL bottleneck.
for (int i = 0; i <= sizeOfReadArray - 1; i++)
{
arrPreInitializedDpForRead.Add(AX_DP_Create_Preinitialized());
}
//create many Products and add to a static array for update. This avoids a SQL bottleneck.
for (int i = 0; i <= sizeOfUpdateArray - 1; i++)
{
arrPreInitializedDpForUpdate.Add(AX_DP_Create_Preinitialized());
}
} // end class initialize
# region Distinct Product Tests
static CallContext context_S = new CRUD.AX_WS_Services.CallContext();
static EcoResProductServiceClient client_S = new EcoResProductServiceClient(); // this opens and closes the client. Service channels might be better.
/// <summary>
/// This approaches uses the big AX port.
/// </summary>
[TestMethod]
public void AX_DP_Create()
{
context = new CRUD.AX_WS_Services.CallContext();
context.Company = axTestCompany;
// This gives us access to properties in the LoadTest object. Need this to get the user (thread) ID.
LoadTestUserContext loadTestUserContext =
this.TestContext.Properties["$LoadTestUserContext"] as LoadTestUserContext;
if (TestContext.Properties.Contains("$LoadTestUserContext"))
{
int uID = loadTestUserContext.UserId;
}
// Set up to make sure we do not create duplicates under high load.
Guid DMSStaticGUID = GetDMSStaticGUID();
string strTheGuid = DMSStaticGUID.ToString().Substring(0, 20);
Interlocked.Increment(ref sequential);
//string agentId = testContextInstance.Properties["AgentId"].ToString();
DateTime curDT = DateTime.Now;
curDT.ToString("mm dd");
string nameAndProdNum = "Ch" + curDT + strTheGuid + "_" + sequential.ToString(); //+ agentId + "_"
// --------------------------------------------
CRUD.AX_WS_Services.AxdEntity_Product_EcoResDistinctProduct dp =
new CRUD.AX_WS_Services.AxdEntity_Product_EcoResDistinctProduct();
dp.DisplayProductNumber = nameAndProdNum;
dp.SearchName = nameAndProdNum;
dp.ProductType = CRUD.AX_WS_Services.AxdEnum_EcoResProductType.Service;
dp.Translation = new CRUD.AX_WS_Services.AxdEntity_Translation[1];
dp.Translation[0] = new CRUD.AX_WS_Services.AxdEntity_Translation()
{
LanguageId = "en-us",
Name = "DMS Perf"
};
dp.Identifier = new CRUD.AX_WS_Services.AxdEntity_Identifier[1];
dp.Identifier[0] = new CRUD.AX_WS_Services.AxdEntity_Identifier()
{
ProductNumber = strTheGuid,
};
AxdEntity_DMSProductClassProduct productClassProduct = new AxdEntity_DMSProductClassProduct();
productClassProduct.DMSProductClass = prodClassNameForTest; // productClass.Name;
// productClassProduct.Product = productMaster.DisplayProductNumber;
dp.DMSProductClassProduct = new CRUD.AX_WS_Services.AxdEntity_DMSProductClassProduct[1] { productClassProduct };
AxdEcoResProduct doc = new AxdEcoResProduct();
doc.Product = new AxdEntity_Product_EcoResProduct[1] { dp };
testContextInstance.BeginTimer("AX_DP_Create");
CRUD.AX_WS_Services.EntityKey[] keys = client.create(context, doc);
testContextInstance.EndTimer("AX_DP_Create");
client.Close();
}
/// <summary>
/// Read only of the RecID
/// </summary>
[TestMethod]
public void AX_DP_Read()
{
context = new CRUD.AX_WS_Services.CallContext();
context.Company = axTestCompany;
// march sequentially through the pre-created entities for read.
if (numberTimesReadExecuted >= sizeOfReadArray - 1)
{
numberTimesReadExecuted = 0;
}
Object dpSingleObj = arrPreInitializedDpForRead[numberTimesReadExecuted];
Interlocked.Increment(ref numberTimesReadExecuted);
CRUD.AX_WS_Services.EntityKey[] myKey = (CRUD.AX_WS_Services.EntityKey[])dpSingleObj;
TestContext.BeginTimer("AX_DP_Read");
client.read(context, myKey); // we created keyToRead Statically in classInitailize
TestContext.EndTimer("AX_DP_Read");
client.Close();
}
/// <summary>
/// To do: Investigate if it is possible to do the update without the read. I think it is by partial updates?
/// </summary>
[TestMethod]
public void AX_DP_Update_With_Read()
{
context = new CRUD.AX_WS_Services.CallContext();
//client = new EcoResProductServiceClient(); // this opens and closes the client. Service channels might be better.
context.Company = axTestCompany;
AxdEcoResProduct DPtoRead = new AxdEcoResProduct();
CRUD.AX_WS_Services.CallContext callcontext = new CRUD.AX_WS_Services.CallContext();
callcontext.Company = axTestCompany;
callcontext.Language = "en-us";
// march sequentially through the pre-created entities for read.
if (numberTimesUpdateExecuted >= sizeOfReadArray - 1)
{
numberTimesUpdateExecuted = 0;
}
Object dpSingleObj = arrPreInitializedDpForRead[numberTimesUpdateExecuted];
Interlocked.Increment(ref numberTimesUpdateExecuted);
CRUD.AX_WS_Services.EntityKey[] myKey = (CRUD.AX_WS_Services.EntityKey[])dpSingleObj;
TestContext.BeginTimer("AX_DP_Update");
DPtoRead = client.read(context, myKey);
TestContext.EndTimer("AX_DP_Update");
// now try to update it ---------------
AxdEcoResProduct DPtoUpdate = DPtoRead;
DPtoUpdate.Product[0].SearchName = "LoadTestUpdated";
TestContext.BeginTimer("AX_DP_Update");
client.update(context, myKey, DPtoUpdate);
TestContext.EndTimer("AX_DP_Update");
client.Close();
}
/// <summary>
///
/// </summary>
[TestMethod]
public void AX_DP_Delete()
{
context = new CRUD.AX_WS_Services.CallContext();
//client = new EcoResProductServiceClient(); // this opens and closes the client. Service channels might be better.
context.Company = axTestCompany;
// must create a product in order to delete it. Only way around this is to create a lot of enties, get their ID's, and
// add a data source to this test.
CRUD.AX_WS_Services.EntityKey[] keyToDelete = AX_DP_Create_Preinitialized();
Thread.Sleep(1000);
TestContext.BeginTimer("AX_DP_Delete");
client.delete(context, keyToDelete);
TestContext.EndTimer("AX_DP_Delete");
client.Close();
}
public DataSet AX_DP_Query_Count_0001_DataSet(string displayProdNum)
{
DataSet ds = AX_DP_Query_Parameterized(1, true, displayProdNum);
return ds;
}
[TestMethod]
public void AX_DP_Query_Count_0001()
{
AX_DP_Query_Parameterized(1, true);
}
[TestMethod]
public void AX_DP_Query_Count_0050()
{
AX_DP_Query_Parameterized(50, true);
}
[TestMethod]
public void AX_DP_Query_Count_0100()
{
AX_DP_Query_Parameterized(100, true);
}
[TestMethod]
public void AX_DP_Query_Count_0250()
{
AX_DP_Query_Parameterized(250, true);
}
[TestMethod]
public void AX_DP_Query_Count_0500()
{
AX_DP_Query_Parameterized(500, true);
}
[TestMethod]
public void AX_DP_Query_Count_1000()
{
AX_DP_Query_Parameterized(1000, true);
}
[TestMethod]
public void AX_DP_Query_Count_5000()
{
AX_DP_Query_Parameterized(5000, true);
}
/// <summary>
/// Retrieve X number of records from AX.
/// Note that this is not a test method. Execute this method by means of the above test methods.
/// Areas for further investigation:
/// What about "static" queries -- already defined in the AOT
// http://msdn.microsoft.com/en-us/library/gg847959.aspx/html
/// </summary>
public DataSet AX_DP_Query_Parameterized(int querySize, bool dynFieldList, string displayProdNum = null)
{
// Some references
// http://community.dynamics.com/ax/b/axpulse/archive/2012/11/11/microsoft-dynamics-ax-2012-aif-query-service.aspx#.UfxoKdjn_Qg
// more about filters, such as by company.
//http://axcalated.blogspot.com/2015/06/c-ax-aif-webservice-apply-company.html
// other interesting things
// return flattened data set: https://msdn.microsoft.com/EN-US/library/gg841671.aspx
// Odata query service: http://blogs.msdn.com/b/aif/archive/2011/08/23/odata-query-service.aspx
// better way to formate the query: http://www.sohua.xyz/questions-full/1773870/how-to-format-datetime-comparison-value-in-ax-query-service
// Paging paging = null;
// Paging paging = new PositionBasedPaging { StartingPosition = 1, NumberOfRecordsToFetch = 50 }; // only works if an order by is present
Paging paging = new ValueBasedPaging() { RecordLimit = querySize };
QueryServiceClient client = new QueryServiceClient();
// if these are blank, user executing VS will be used.
client.ClientCredentials.Windows.ClientCredential.UserName = AX_Proxy_Initialize.userName;
client.ClientCredentials.Windows.ClientCredential.Password = AX_Proxy_Initialize.userPassword;
client.ClientCredentials.Windows.ClientCredential.Domain = AX_Proxy_Initialize.userDomain;
// Meta data. This can be captured from the eventlog of CRM.
QueryMetadata queryMetaData = new QueryMetadata();
// Set the meta data properties of the query.
queryMetaData.QueryType = QueryType.Join;
queryMetaData.AllowCheck = true;
queryMetaData.JoinPolymorphicTables = true;
queryMetaData.EnableOnlyModified = false;
queryMetaData.Literals = Literals.Default;
queryMetaData.QueryType = QueryType.Join;
queryMetaData.ReturnFlatDataSet = false;
queryMetaData.ReturnOnlyVisible = false;
queryMetaData.Searchable = false;
// Datasource node.
queryMetaData.DataSources = new QueryDataSourceMetadata[1];
QueryDataSourceMetadata dataSource;
// Set the properties of the data source (AX table).
dataSource = new QueryDataSourceMetadata();
dataSource.Name = "Ecoresproduct"; // This is the name of a field in the table. However it is best if it is this is a primary key, or else zero records will be returned.
dataSource.Table = "Ecoresproduct"; // name of the table in AX
dataSource.Enabled = true;
dataSource.DynamicFieldList = dynFieldList; //dynFieldList; // Setting DynamicFieldList property to true returns all fields. However if you set it to false without specifiying fields it will timeout AX.
//define the current company to search by within AX
//dataSource.Company = "NML";
queryMetaData.AllowCrossCompany = true;
dataSource.TableId = 0;
dataSource.Enabled = true;
dataSource.ConcurrencyModel = ConcurrencyModel.Auto;
dataSource.FetchMode = FetchMode.OneToOne;
dataSource.JoinMode = JoinMode.InnerJoin;
dataSource.OrderMode = OrderMode.OrderBy;
dataSource.ExpansionType = ExpansionType.Original;
dataSource.FirstFast = false;
dataSource.FirstOnly = false;
dataSource.HasRelations = false;
dataSource.SelectWithRepeatableRead = false;
dataSource.UnionType = UnionType.None;
dataSource.Update = false;
//Add the data source to the query.
queryMetaData.DataSources[0] = dataSource;
//// this is how you do a where clause. Should not be used be used for testing.
//// notice the if statement. Will not execute unless a recID is passed in.
//if (displayProdNum != null)
//{
// var range = new QueryDataRangeMetadata
// {
// TableName = "Ecoresproduct",
// FieldName = "RecId",
// Value = displayProdNum,
// Enabled = true
// };
// dataSource.Ranges = new QueryRangeMetadata[] { range };
//}
// execute the query.
string timerName = ("AX_DP_Query_" + querySize.ToString("D4") + "_dynFld_" + dynFieldList);
testContextInstance.BeginTimer(timerName);
DataSet ds = client.ExecuteQuery(queryMetaData, ref paging);
testContextInstance.EndTimer(timerName);
client.Close();
//// verification steps
//int thecount = ds.Tables[0].Rows.Count;
//foreach (DataRow row in ds.Tables[0].Rows)
//{
// int theCount = ds.Tables[0].Rows.Count;
// string prodNumStr = ds.Tables[0].Rows[0]["DisplayProductNumber"].ToString();
//}
////UnpackResponses.PrintAXRows(ds); // debugging only do not use under load
//// Verification #2
//int numOfTables = ds.Tables.Count;
//int numOfRowsFromAllTables = 0;
//int numOfColumnsFromAllTables = 0;
//for (int i = 0; i < ds.Tables.Count; i++)
//{
// numOfRowsFromAllTables += ds.Tables[i].Rows.Count;
// numOfColumnsFromAllTables += ds.Tables[i].Columns.Count;
//}
return ds;
}
// End of CRUD methods -------------------------------------------------------------------------
#endregion Distinct Product Tests
// helper methods ---------------------------
/// <summary>
/// This create is for use in the update, query and read methods.
/// </summary>
/// <returns></returns>
public static CRUD.AX_WS_Services.EntityKey[] AX_DP_Create_Preinitialized() //string prodClassNameForTest = null
{
// AX_Proxy_Initialize aXProxy = new AX_Proxy_Initialize();
Guid DMSStaticGUID = GetDMSStaticGUID();
string strTheGuid = DMSStaticGUID.ToString().Substring(0, 20);
//DMSPerfTest.CRUD.AX_WS_Services.CallContext context = new DMSPerfTest.CRUD.AX_WS_Services.CallContext();
//EcoResProductServiceClient //client = new EcoResProductServiceClient();
CRUD.AX_WS_Services.AxdEntity_Product_EcoResDistinctProduct dp = new CRUD.AX_WS_Services.AxdEntity_Product_EcoResDistinctProduct();
dp.DisplayProductNumber = strTheGuid;
dp.SearchName = strTheGuid;
dp.ProductType = CRUD.AX_WS_Services.AxdEnum_EcoResProductType.Service;
dp.Translation = new CRUD.AX_WS_Services.AxdEntity_Translation[1];
dp.Translation[0] = new CRUD.AX_WS_Services.AxdEntity_Translation()
{
LanguageId = "en-us",
Name = "Nissan Test 2"
};
dp.Identifier = new CRUD.AX_WS_Services.AxdEntity_Identifier[1];
dp.Identifier[0] = new CRUD.AX_WS_Services.AxdEntity_Identifier()
{
ProductNumber = strTheGuid,
};
AxdEntity_DMSProductClassProduct productClassProduct = new AxdEntity_DMSProductClassProduct();
productClassProduct.DMSProductClass = productClass.Name; // ; GetProductClassReference().Name; // could do this name but introduces extra call every request.
//productClassProduct.Product = productMaster.DisplayProductNumber;
dp.DMSProductClassProduct = new CRUD.AX_WS_Services.AxdEntity_DMSProductClassProduct[1] { productClassProduct };
AxdEcoResProduct doc = new AxdEcoResProduct();
doc.Product = new AxdEntity_Product_EcoResProduct[1] { dp };
CRUD.AX_WS_Services.EntityKey[] oneCreatedDPkey = client.create(context, doc);
return oneCreatedDPkey;
}
[TestMethod]
public void Debug_AX_DP_Create_Many_Products_And_Query_Tester()
{
AX_DP_Create_Many_Products_And_Query(50);
}
/// <summary>
/// This create is for use in the update, query and read methods.
/// </summary>
/// <returns></returns>
public List<string> AX_DP_Create_Many_Products_And_Query(int numToCreate) //string prodClassNameForTest = null
{
List<CRUD.AX_WS_Services.EntityKey[]> listOfKeys = new List<CRUD.AX_WS_Services.EntityKey[]>();
TestContext.BeginTimer("Create50");
for (int i = 0; i < numToCreate; i++)
{
CRUD.AX_WS_Services.EntityKey[] key = AX_DP_Create_Preinitialized();
listOfKeys.Add(key);
}
TestContext.EndTimer("Create50");
// we now must prepare to query recids from AX because the create method does not return display name.
// we need display name (which is unique) to query from the CRM side
// set up to call query
AX_Distinct_Product axdp = new AX_Distinct_Product();
List<string> displayProdNums = new List<string>();
TestContext.BeginTimer("Query50");
foreach (CRUD.AX_WS_Services.EntityKey[] key in listOfKeys)
{
string recID = key[0].KeyData[0].Value.ToString();
DataSet ds = AX_DP_Query_Count_0001_DataSet(recID);
// there is a better way to do this here with retrieve multiple. Have to think
//for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
//{
string prodNumStr = ds.Tables[0].Rows[0]["DisplayProductNumber"].ToString();
// works but the above is more clear
string displayName = "";
var theData = ds.Tables[0].Rows[0].ItemArray;
foreach (DataRow row in ds.Tables[0].Rows)
{
displayName = theData[1].ToString();
// int theCount = ds.Tables[0].Rows[0].Table.Columns[0].;
}
displayProdNums.Add(displayName);
//}
}
TestContext.EndTimer("Query50");
return displayProdNums;
}
/// <summary>
/// Helper Method to read product class (once) and use it repeatedly in tests.
/// </summary>
/// <returns></returns>
public static AxdEntity_DMSProductClass GetProductClassReference()
{
CRUD.AX_WS_Services.CallContext context = new CRUD.AX_WS_Services.CallContext();
ProductClassServiceClient productClassService = new ProductClassServiceClient();
context.Company = axTestCompany;
productClassService.ClientCredentials.Windows.ClientCredential.Password = AX_Proxy_Initialize.userPassword;
productClassService.ClientCredentials.Windows.ClientCredential.UserName = AX_Proxy_Initialize.userName;
productClassService.ClientCredentials.Windows.ClientCredential.Domain = AX_Proxy_Initialize.userDomain;
CRUD.AX_WS_Services.QueryCriteria criteria = new CRUD.AX_WS_Services.QueryCriteria();
criteria.CriteriaElement = new CRUD.AX_WS_Services.CriteriaElement[1];
criteria.CriteriaElement[0] = new CRUD.AX_WS_Services.CriteriaElement();
criteria.CriteriaElement[0].DataSourceName = "DMSProductClass";
criteria.CriteriaElement[0].FieldName = "Name";
criteria.CriteriaElement[0].Operator = CRUD.AX_WS_Services.Operator.Equal;
criteria.CriteriaElement[0].Value1 = "Kit"; //"New vehicle - color only";
CRUD.AX_WS_Services.EntityKey[] keys = productClassService.findKeys(context, criteria);
AxdEntity_DMSProductClass productClass = new AxdEntity_DMSProductClass();
AxdProductClass doc = productClassService.read(context, keys);
return doc.DMSProductClass[0];
}
static Guid DMSStaticGUID;
public static Guid GetDMSStaticGUID()
{
DMSStaticGUID = Guid.NewGuid();
return DMSStaticGUID;
}
} // end class