Hi again.
As I mentioned before, I will be making changes and updates to this project on a regular basis, and last night I made some decent progress toward being generic and reusable when it comes to populating the data class. Of course I will share.
We are going to rework our Data Access Layer (DAXDataAccessLayer), and remove a good amount of redundancy out of our data class object, AddressState.
First off, add a new class to your project, and name it DAXRecordReader. Add the following code..
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Reflection;
using System.Data.Common;
using Microsoft.Dynamics.BusinessConnectorNet;
namespace LinqToAX
{
internal class DAXRecordReader<T> : IEnumerable<T>, IEnumerable where T : class, new()
{
Enumerator enumerator;
internal DAXRecordReader(AxaptaRecord aRecord)
{
this.enumerator = new Enumerator(aRecord);
}
public IEnumerator<T> GetEnumerator()
{
Enumerator e = this.enumerator;
if (e == null)
{
throw new InvalidOperationException("Cannot enumerate more than once");
}
this.enumerator = null;
return e;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
class Enumerator : IEnumerator<T>, IEnumerator, IDisposable
{
AxaptaRecord aRecord;
PropertyInfo[] fields;
T current;
internal Enumerator(AxaptaRecord aRecord)
{
this.aRecord = aRecord;
this.fields = typeof(T).GetProperties();
}
public T Current
{
get { return this.current; }
}
object IEnumerator.Current
{
get { return this.current; }
}
public bool MoveNext()
{
if (this.aRecord.Found)
{
if (this.fields == null)
{
this.InitFieldLookup();
}
T instance = new T();
for (int i = 0; i < fields.Count(); i++)
{
PropertyInfo pi = fields[i];
pi.SetValue(instance, this.aRecord.get_Field(fields[i].Name),null);
}
this.current = instance;
aRecord.Next();
return true;
}
return false;
}
public void Reset()
{
}
public void Dispose()
{
this.aRecord.Dispose();
}
private void InitFieldLookup()
{
fields = this.current.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
}
}
}
}
What we are doing here is using reflection to get the fields collection off of our Type, removing the need to hard code it, or generate it. It also removes the need to create an instance of our type on the fly and invoke a method on it to do the same work. This class exists at design and is of course generic. This meets our need better...
Oh well, now to make the change to our DAXDataAccessLayer.AxExecQuery method to use it.
Oh well, now to make the change to our DAXDataAccessLayer.AxExecQuery method to use it.
public AxaptaRecord AxExecQuery(string query, Type objectType)
{
AxaptaRecord ar;
lock (lockAxObject)
{
try
{
ar = staticAxapta.CreateAxaptaRecord(objectType.Name);
ar.ExecuteStmt(query);
return ar;
}
catch (Exception e)
{
DAXUtils.LogEvent("DAXDataAccessLayer", "Business Connector execQuery Failed! " + e.Message, EventLogEntryType.Error);
ResetStaticLogon();
throw e;
}
}
}
And finally, lets change our DAXQueryProvider.Execute method to get rid of the data reader stuff and use our AxaptaRecord object filler-upper instead...
public override object Execute(Expression expression)
{
AxaptaRecord aRecord;
string query = this.Translate(expression);
Type elementType = TypeSystem.GetElementType(expression.Type);
aRecord = connection.AxExecQuery(query, elementType);
return Activator.CreateInstance(typeof(DAXRecordReader<>).MakeGenericType(elementType), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { aRecord }, null);
}
There, much cleaner, less overhead, all better. Oh, you can remove the MakeDataTable method out of your test data class, and any others you have probably built by now, it's no longer used of course.
More later, be good.
H
{
AxaptaRecord ar;
lock (lockAxObject)
{
try
{
ar = staticAxapta.CreateAxaptaRecord(objectType.Name);
ar.ExecuteStmt(query);
return ar;
}
catch (Exception e)
{
DAXUtils.LogEvent("DAXDataAccessLayer", "Business Connector execQuery Failed! " + e.Message, EventLogEntryType.Error);
ResetStaticLogon();
throw e;
}
}
}
And finally, lets change our DAXQueryProvider.Execute method to get rid of the data reader stuff and use our AxaptaRecord object filler-upper instead...
public override object Execute(Expression expression)
{
AxaptaRecord aRecord;
string query = this.Translate(expression);
Type elementType = TypeSystem.GetElementType(expression.Type);
aRecord = connection.AxExecQuery(query, elementType);
return Activator.CreateInstance(typeof(DAXRecordReader<>).MakeGenericType(elementType), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { aRecord }, null);
}
There, much cleaner, less overhead, all better. Oh, you can remove the MakeDataTable method out of your test data class, and any others you have probably built by now, it's no longer used of course.
More later, be good.
H
No comments:
Post a Comment