I am using C# to create a SQL Server view, then open an access database and link the table into access. The create view statement, open database statement and link statement work great BUT the catch here is it will always link the table as read-only. What piece o'code do I need to add or update current so that the view is not always linked as read-only?
string MasterDatabase = "R:\\Testing\\MasterDatabase.mdb";
DAO.Database dd;
DAO.DBEngine db = new DAO.DBEngine();
DAO.TableDef tdf9;
bool found = false;
DAO.TableDef tdf1;
string Table = "ServiceEntranceLog";
string TableAccess = "Service_Entrance_Log";
using (var connection = new SqlConnection(ConnectionStringHere))
using (var command = connection.CreateCommand())
{
using (var command4 = connection.CreateCommand())
{
command4.CommandText = "CREATE VIEW HelperView" AS SELECT * FROM monster.ServiceEntranceLog";
command4.ExecuteNonQuery();
}
}
if (_combobox1.SelectedItems.Contains("MasterDatabase"))
{
dd = db.OpenDatabase(CRDB);
try
{
string[] tableNames = new string[1] { TableAccess };
for (int q = tableNames.GetLowerBound(0); q <= tableNames.GetUpperBound(0); q++)
{
foreach (DAO.TableDef tabledef in dd.TableDefs)
{
string name = tableNames[q];
if (tabledef.Name == name) { found = true; }
try { if (found) { dd.TableDefs.Delete(name); } }
catch { }
}
}
}
catch { }
tdf1 = dd.CreateTableDef(TableAccess);
tdf1.Connect = connectionString;
tdf1.SourceTableName = Table;
dd.TableDefs.Append(tdf1);
}
Alritey, so it seems the issue is I needed to define a primary key when linking in the table into access so that the table would be updateable. Using this syntax does the trick
dd.Execute "CREATE UNIQUE INDEX SomeIndex ON SomeTable (PrimaryKeyColumn) WITH PRIMARY"
I have this code running calling the data from the core system,
public int MuatTurunMTS(hopesWcfRef.Manifest2 entParam, string destination, int capacity)
{
try
{
EL.iSeriesWcf.IiSeriesClient iClient = new iSeriesWcf.IiSeriesClient();
EL.iSeriesWcf.Manifest2 manifest2 = new iSeriesWcf.Manifest2();
manifest2 = iClient.GetManifest2(entParam.NOKT, destination, capacity);
DataTable dt = new DataTable();
dt = manifest2.Manifest2Table;
List<hopesWcfRef.Manifest2> LstManifest2 = new List<hopesWcfRef.Manifest2>();
for (int i = 0; i < dt.Rows.Count; i++)
{
hopesWcfRef.Manifest2 newDataRow = new hopesWcfRef.Manifest2();
newDataRow.NOPASPOT = dt.Rows[i][1].ToString();
newDataRow.NOKT = dt.Rows[i][0].ToString();
newDataRow.KOD_PGKT = dt.Rows[i][2].ToString();
newDataRow.KOD_KLGA = dt.Rows[i][3].ToString();
newDataRow.NAMA = dt.Rows[i][4].ToString();
newDataRow.TKHLAHIR = (dt.Rows[i][5].ToString() == "1/1/0001 12:00:00 AM" ? DateTime.Now : Convert.ToDateTime(dt.Rows[i][5])); //Convert.ToDateTime(dt.Rows[i][5]);
newDataRow.JANTINA = dt.Rows[i][6].ToString();
newDataRow.WARGANEGARA = dt.Rows[i][7].ToString();
newDataRow.JNS_JEMAAH = dt.Rows[i][8].ToString();
newDataRow.NO_SIRI = dt.Rows[i][9].ToString();
newDataRow.NO_MS = Convert.ToInt16(dt.Rows[i][10]);
newDataRow.BARKOD = dt.Rows[i][13].ToString();
newDataRow.NO_DAFTAR = dt.Rows[i][14].ToString();
//bydefault make cell empty
newDataRow.STS_JEMAAH = "";
newDataRow.SEAT_NO = "";
newDataRow.SEAT_ZONE = "";
newDataRow.BERAT = 0;
newDataRow.JUM_BEG = 0;
LstManifest2.Add(newDataRow);
}
int cntr = 0;
if (LstManifest2.Count != 0)
{
foreach (hopesWcfRef.Manifest2 manifest in LstManifest2)
{
cntr++;
SaveManifestTS(manifest);
}
}
return LstManifest2.Count;
}
catch (Exception ex)
{
throw ex;
}
}
And goes to :
public void SaveManifestTS(hopesWcfRef.Manifest2 manifest)
{
try
{
ent.BeginSaveChanges(SaveChangesOptions.Batch, null, null);
ent.AddObject("Manifest2", manifest);
ent.SaveChanges(SaveChangesOptions.Batch);
}
catch (Exception ex)
{
//Uri u = new Uri(ent.BaseUri + "GetErrorMsg"
// , UriKind.RelativeOrAbsolute);
//string datas = (ent.Execute<string>(u)).FirstOrDefault();
//Exception ex = new Exception(datas);
throw ex;
}
}
When SaveChanges run, if the data exist it will duplicate the entire row,
How to avoid the data being duplicate when insert (savechanges)??????
Many Thanks
What about: Do not insert.
In these cases I am using a MERGE statement that updates existing data (based on primary key) and inserts new data.
Oh, and all the code example you loved to post is totally irrelevant to the question, which is a pure SQL side question. You literally quote your car manual then asking which direction to turn.
I have a web method in a c# web service which creates three lists, which are filled from xml input. I want to combine these three lists into one entity (a DataSet would be the best, as the iOS app that is consuming this web service is already programmed to accept and parse DataSets), and return them from the web method.
Here is currently what my code looks like:
[WebMethod]
public DataSet SelectObjects(string ExternalID, string Password)
{
DataSet ds = new DataSet();
MembershipAuthServiceReference.MembershipAuthenticationService objService = new MembershipAuthServiceReference.MembershipAuthenticationService();
MembershipAuthServiceReference.SoapHeaderCredentials objSoapHeader = new MembershipAuthServiceReference.SoapHeaderCredentials();
MembershipAuthServiceReference.MemberUserInfo objMemberInfo = new MembershipAuthServiceReference.MemberUserInfo();
try
{
objSoapHeader.UserName = ExternalID;
objSoapHeader.Password = Password;
objMemberInfo = objService.GetMembershipInfo();
List<Obj1> ListObj1 = new List<Obj1>();
for (int i = 0; i < objMemberInfo.Obj1.Length; i++)
{
Obj1 obj_Obj1 = new Obj1();
obj_Obj1.Stuff = objMemberInfo.Obj1[i].Stuff.ToString();
ListObj1.Add(obj_Obj1);
}
List<Obj2> ListObj2 = new List<Obj2>();
for (int i = 0; i < objMemberInfo.Obj2.Length; i++)
{
Obj2 obj_Obj2 = new Obj2();
obj_Obj2.Stuff = objMemberInfo.Obj2[i].Stuff.ToString();
ListObj2.Add(obj_Obj2);
}
List<Obj3> ListObj3 = new List<Obj3>();
for (int i = 0; i < objMemberInfo.Obj3.Length; i++)
{
Obj3 obj_Obj3 = new Obj3();
obj_Obj3.Stuff = objMemberInfo.Obj3[i].Stuff.ToString();
ListObj3.Add(obj_Obj3);
}
}
catch (Exception ex)
{
string sError;
sError = ex.Message.ToString();
}
return ds;
}
How do I combine these lists into a DataSet? I'm assuming it's possible? If not, is there a viable alternative that does the same thing?
First concatenate your lists as shown below and then use the link to generate the dataset
var combinedList= ListObj1.Concat(ListObj2).Concat(ListObj3);
How do I transform a List<T> into a DataSet?
mst + .msi table information.
I created following function to read msi Tables
// This method returns all rows and columns of a Table specified by Name
public DataTable ReadMsiTableByName(string msiFile, string tableName)
{
DataTable msiTable = new DataTable(tableName);
Database database = null;
View view = null;
try
{
using (database = new Database(msiFile, DatabaseOpenMode.ReadOnly))
{
string sqlQuery = String.Format("SELECT * FROM {0}", tableName);
view = database.OpenView(sqlQuery);
view.Execute(null);
Record record = view.Fetch();
ColumnCollection columnCollection = view.Columns;
for (int i = 0; i < columnCollection.Count; i++)
{
string columnName = columnCollection[i].Name.ToString();
System.Type columnType = columnCollection[i].Type;
msiTable.Columns.Add(columnName, columnType.UnderlyingSystemType);
}
while (record != null)
{
DataRow row = msiTable.NewRow();
for (int i = 0; i < columnCollection.Count; i++)
{
string type = columnCollection[i].Type.ToString();
if (type == "System.String")
{
row[columnCollection[i].Name.ToString()] = record.GetString(columnCollection[i].Name.ToString());
}
else if (type == "System.Int16")
{
row[columnCollection[i].Name.ToString()] = record.GetInteger(columnCollection[i].Name.ToString());
}
else if (type == "System.Int32")
{
row[columnCollection[i].Name.ToString()] = record.GetInteger(columnCollection[i].Name.ToString());
}
else if (type == "System.IO.Stream")
{
System.IO.Stream stream;
stream = record.GetStream(columnCollection[i].Name.ToString());
row[columnCollection[i].Name.ToString()] = stream;
}
}
msiTable.Rows.Add(row);
record = view.Fetch();
}
}
}
catch (Exception ex)
{
CommonFn.CreateLog(ex.ToString());
}
finally
{
if (database != null)
database.Close();
if (view != null)
view.Close();
}
return msiTable;
}
However I am unable to read .mst with this function. I read that you need to use msi transform for it, But I don't want to change the content of msi or mst, I just need to read all the tables. Please point me in right direction. Thanks in Advance :)
An MST is by definition a transform. It only contains the deltas of a base MSI.
The database class has a method called ViewTransform. If the MST is compatible with the MSI it'll succeed and the changes appear in the _TransformView table.
Alternatively if you don't want to see whats changed but you want to see the final state, you could copy the MSI to a temporary MSI and use the ApplyTransform method to apply the transform and then commit it. Now you could query it using the code you already have.
Works for me:
msiDatabase = new Database(#"Foo.msi", DatabaseOpenMode.ReadOnly);
msiDatabase.ApplyTransform(#"Foo.mst");
I'm writing a C# command line tool to fetch data from AX and to add data (create new tables) to AX.
Fetching data from an AX table is easy and documented here: http://msdn.microsoft.com/en-us/library/cc197126.aspx
Adding data to an existing table is also easy: http://msdn.microsoft.com/en-us/library/aa868997.aspx
But I cannot figure out how to do two things:
Create a new AX Table
Retrieve data from an AX Query
Can someone please share some sample code or give some pointers on where to start looking. My searches on Google and MSDN have not revealed much.
NOTE: I am not an experienced AX or ERP developer.
I have created a query in the AOT and was able to use C# to return the data. Find the code below. It's a query that returns the sales that I create Aging Buckets with. I hope this helps.
[DataMethod(), AxSessionPermission(SecurityAction.Assert)]
public static System.Data.DataTable GetCustBuckets(String AccountNum)
{
//Report Parameters
Dictionary<string, object> d = new Dictionary<string, object>();
d.Add("CustTransOpen.AccountNum",AccountNum);
// Create a data table. Add columns for item group and item information.
DataTable table = new DataTable();
table = AxQuery.ExecuteQuery("SELECT * FROM epcCustomerAging",d);
DataTable tableBucket = new DataTable();
DataRow rowBucket;
tableBucket.Columns.Add("Current", typeof(double));
tableBucket.Columns.Add("Bucket31to60", typeof(double));
tableBucket.Columns.Add("Bucket61to90", typeof(double));
tableBucket.Columns.Add("Bucket91to120", typeof(double));
tableBucket.Columns.Add("Over120", typeof(double));
//Variables to hold BUCKETS
double dCurrent = 0;
double dBucket31to60 = 0;
double dBucket61to90 = 0;
double dBucket91to120 = 0;
double dOver120 = 0;
// Iterate through the results. Add the item group to the data table. Call the display method
foreach (DataRow TransRow in table.Rows)
{
DateTime TransDate = Convert.ToDateTime(TransRow["TransDate"].ToString());
double AmountCur = Convert.ToDouble(TransRow["AmountCur"].ToString());
DateTime Today= Microsoft.VisualBasic.DateAndTime.Now;
long nDays = Microsoft.VisualBasic.DateAndTime.DateDiff(Microsoft.VisualBasic.DateInterval.Day, TransDate, Today, 0, 0);
if (nDays <= 30)
{
dCurrent += AmountCur;
}
else if (nDays <= 60)
{
dBucket31to60 += AmountCur ;
}
else if (nDays <= 90)
{
dBucket61to90 += AmountCur;
}
else if (nDays <= 120)
{
dBucket91to120 += AmountCur;
}
else
{
dOver120 += AmountCur;
}
}
rowBucket = tableBucket.NewRow();
rowBucket["Current"] = dCurrent;
rowBucket["Bucket31to60"] = dBucket31to60;
rowBucket["Bucket61to90"] = dBucket61to90;
rowBucket["Bucket91to120"] = dBucket91to120;
rowBucket["Over120"] = dOver120;
tableBucket.Rows.Add(rowBucket);
return tableBucket;
}
Here is a way to create a new AX table from C# (this is using an extension method):
public static bool CreateAXTable(this Axapta ax)
{
string TableName = "MyCustomTable";
string size = "255"; //You could load this from a setting
bool val = false;
if (!ax.TableExists(TableName))
{
AxaptaObject TablesNode = (AxaptaObject)ax.CallStaticClassMethod("TreeNode", "findNode", #"\Data Dictionary\Tables");
AxaptaObject node;
AxaptaObject fields;
AxaptaObject fieldNode;
TablesNode.Call("AOTadd", TableName);
node = (AxaptaObject)ax.CallStaticClassMethod("TreeNode", "findNode", "\\Data dictionary\\Tables\\" + TableName);
fields = (AxaptaObject)ax.CallStaticClassMethod("TreeNode", "findNode", "\\Data dictionary\\Tables\\" + TableName + "\\Fields");
fields.Call("addString", "String1"); //add a string field
fieldNode = (AxaptaObject)fields.Call("AOTfindChild", "String1"); //grab a reference to the field
fieldNode.Call("AOTsetProperty", "StringSize", size);
fieldNode.Call("AOTsave");
fields.Call("addString", "String2"); //add a string field
fieldNode = (AxaptaObject)fields.Call("AOTfindChild", "String2"); //grab a reference to the field
fieldNode.Call("AOTsetProperty", "StringSize", size);
fieldNode.Call("AOTsave");
fields.Call("addString", "String3"); //add a string field
fieldNode = (AxaptaObject)fields.Call("AOTfindChild", "String3"); //grab a reference to the field
fieldNode.Call("AOTsetProperty", "StringSize", size);
fieldNode.Call("AOTsave");
fields.Call("addString", "String4"); //add a string field
fieldNode = (AxaptaObject)fields.Call("AOTfindChild", "String4"); //grab a reference to the field
fieldNode.Call("AOTsetProperty", "StringSize", size);
fieldNode.Call("AOTsave");
fields.Call("addReal", "Real1");
fields.Call("addReal", "Real2");
fields.Call("addReal", "Real3");
fields.Call("addReal", "Real4");
fields.Call("addDate", "Date1");
fields.Call("addDate", "Date2");
fields.Call("addDate", "Date3");
fields.Call("addDate", "Date4");
fields.Call("AOTsave");
node.Call("AOTsave");
AxaptaObject appl = ax.GetObject("appl");
appl.Call("dbSynchronize", Convert.ToInt32(node.Call("applObjectId")), false);
val = true;
}
else //Table already exists
{
val = true;
}
return val;
}
public static bool TableExists(this Axapta ax, string tableName)
{
return ((int)ax.CallStaticClassMethod("Global", "tableName2Id", tableName) > 0);
}
Here is an example of running a query in C#:
(Note: this is a very simplistic method by using an existing query definition, you could also build a query from scratch using QueryBuildDataSource objects, etc...)
Axapta ax = new Axapta();
ax.Logon("", "", "", "");
//Create a query object based on the customer group query in the AOT
AxaptaObject query = ax.CreateAxaptaObject("Query", "CustGroupSRS");
//Create a queryrun object based on the query to fecth records
AxaptaObject queryRun = ax.CreateAxaptaObject("QueryRun", query);
AxaptaRecord CustGroup = null;
;
while (Convert.ToBoolean(queryRun.Call("next")))
{
//GetTableId function is defined here: .Net Business Connector Kernel Functions
CustGroup = (AxaptaRecord)queryRun.Call("get", ax.GetTableId("CustGroup"));
System.Diagnostics.Debug.WriteLine(CustGroup.get_Field("Name").ToString());
}
CustGroup.Dispose();
queryRun.Dispose();
query.Dispose();
ax.Logoff();
ax.Dispose();
I honestly don't think it's possible to create new tables using the business connector. It has to be done within AX and the AOT.
As for returning mixed data, I would probably use a container object for that. Containers can hold sub containers, or axaptarecords. An AxaptaRecord contains data from one defined table.