在AX2012的SSRS报表中可以使用c#或者Visual basic .net编写report data method来获取和操作数据,由report data method返回的数据可以用在报表的表达式中,也可以用作dataset的数据源。
使用Report data method首先需要创建AX model工程,在工程中添加一个报表,双击打开报表,在报表的Data methods节点下右键“Add data method”,设置其名称,右键点击这个添加的data method在菜单中选择“View code”,Visual studio会自动创建一个C#的工程,工程名称为“报表名称.BusinessLogic”,并定义一个和报表名称相同的类,同时自动添加一个DataMethod特性标注的静态方法,类似:
public partial class TestDataMethodReport {[DataMethod(), PermissionSet(SecurityAction.Assert, Name = "FullTrust")]public static string DataMethod1(){throw new NotImplementedException("The method or operation is not implemented.");} }
默认是使用C#工程,要使用Visual basic需要手工设置报表的属性“Data method library”为一个预先添加到AOT中的visual basic的工程,然后就可以在这个VB工程中添加相应的report data method类和方法。
如果要使用report data method作为dataset的数据源,report data method必须返回值类型必须是IEnumerable<DataRow>或者DataTable。返回的数据量可能比较的多,建议使用yield return 返回一个IEnumerable<DataRow>的数据,这要比返回DataTable效率高。report data method可以有参数,这些参数和报表dataset的参数想对应。由于类的名称和报表名称要求相同,所以一个reprot data method只能用在一个报表中,不能被多个报表共享。下面是一个report data method返回数据集的例子:
[DataMethod(), PermissionSet(SecurityAction.Assert, Name = "FullTrust")] public static IEnumerable<DataRow> CreateInventoryItemByRow() {// Define a data table.DataTable table = new DataTable();table.Columns.Add(new DataColumn("ItemID", Type.GetType("System.Int32")));table.Columns.Add(new DataColumn("Name", Type.GetType("System.String")));table.Columns.Add(new DataColumn("Description", Type.GetType("System.String")));table.Columns.Add(new DataColumn("Cost", Type.GetType("System.Decimal")));table.Columns.Add(new DataColumn("SellingPrice", Type.GetType("System.Decimal")));DataRow row = null;row = table.NewRow();row.ItemArray = new object[] { 1734, "Clamp", "Workbench Clamp", 12.48, 17.99 };yield return row;row = table.NewRow();row.ItemArray = new object[] { 1983, "Saw", "Wooden Handle Saw", 7.89, 11.99 };yield return row;row = table.NewRow();row.ItemArray = new object[] { 6728, "Screwdriver", "Standard Screwdriver", 2.75, 3.99 };yield return row;row = table.NewRow();row.ItemArray = new object[] { 4920, "Nails", "Roofing Nails 5lbs", 12.45, 15.99 };yield return row;row = table.NewRow();row.ItemArray = new object[] { 4829, "Tape Measure", "25 ft Tape Measure", 12.87, 16.99 };yield return row;row = table.NewRow();row.ItemArray = new object[] { 2893, "Nails", "Finish Nails", 3.90, 5.99 };yield return row; }
要在报表中使用report data method作为数据源,首先需要编译包含report data method的bussiness logic工程并添加到AOT中。在报表中新建一个dataset,dataset的数据源选择Dynamics AX,Data source type选择bussiness logic,点击Query属性的...按钮就可以选择相应的report data method。包含report的ax model工程也需要编译后添加到AOT,否则也是看不到这个report data method的。
Data report method还可以用在报表的一些表达式中,比如AX自带的Ax model工程CaseReports中用到了这样一个data method:
public partial class Case_MyCases [DataMethod(), PermissionSet(SecurityAction.Assert, Name = "FullTrust")] public static string GetDefaultColorDark(int index) {return ColorHelper.GetDefaultColorDark(index); }
报表chart图形有使用这个方法作为数据系列的颜色:
=GetDefaultColorDark(0)
Report data method使用.net编写可以读取AX以为的数据源,也可以使用custom service来从X++方法获取数据,X++被编译为.net CIL后由custom service宿主供托管代码调用。先在AX中定义一个X++的类:
public class HelloWorld { } [SysEntryPointAttribute(true)] public str printHelloWorld() { return "Hello World"; }
在AOT的service节点下新建一个service,设置Name为MyService,ExternalName为HelloWorldService,选择Class为HelloWorld。在MyService下添加一个service operations,选择方法printHelloWorld。
在AOT的Service group下添加一个名为MyServiceGroup的组,把MyService拖到这个组中,部署MyServiceGroup。
回到Visual studio中的bussinesslogic工程,在工程的reference下添加一个service reference,服务URL类似:http://[machinename]:8101/DynamicsAx/Services/MyServiceGroup。在列出的服务中选择MyServiceGroup添加到工程,删除工程中的app.config文件,编译时app.config重新生成。在bussiness logic工程添加一个新的report data method来测试,注意在cs文件中using相应的service reference:
public static string HelloWorldDataMethod() {// Create an instance of the client proxy class that connects to the service exposed in Microsoft Dynamics AX.var client = AxServiceManagement.CreateServiceClient<HelloWorldServiceClient>("MyServiceGroup");// Call the "printHelloWorld" operation on the service - passing the company that you are testing in.CallContext context = new CallContext();context.Company = "ceu";string returnedValue = client.printHelloWorld(context);// Return the result.return returnedValue; }
在report上新建一个precision的design,添加一个textbox到design,设置其value属性为表达式:
=HelloWorldDataMethod()
预览报表textbox显示#error,VS的输出中有警告信息:
Warning 7 The Value expression for the textrun ‘Textbox1.Paragraphs[0].TextRuns[0]’ contains an error: Unable to find matching endpoint configuration for the passed contract 'HelloWorldService' and port name 'MyServiceGroup' TestDataMethodReport.PrecisionDesign1 [Preview] 0 0
新建了一个控制台的工程来测试这个AIF service,却是能正常得到结果的。不知道问题出在哪里,来回删除service reference重建运行报表也是一样的。意外重启了服务器,再运行报表正常了,...问题又出在那个哪个缓存没更新?!问题还不算完,在VS里预览报表没有问题了,但是在AX通过menu item运行报表或者在SQL report service的页面上运行报表又是一样的错误结果,真是无语啊。查看windows日志有这样的错误:
Object Server 01: An error has occurred in the services framework. Method: AifMessageInspector::AfterReceiveRequest. Error: System.ServiceModel.FaultException: Failed to logon to Microsoft Dynamics AX.
at Microsoft.Dynamics.Ax.Services.AxServiceOperationContext.InitializeSession()
at Microsoft.Dynamics.Ax.Services.AxServiceOperationContext.InitializeContext()
at Microsoft.Dynamics.Ax.Services.AxServiceOperationContext.Attach(OperationContext owner)
at System.ServiceModel.ExtensionCollection`1.InsertItem(Int32 index, IExtension`1 item)
at System.Collections.Generic.SynchronizedCollection`1.Add(T item)
at Microsoft.Dynamics.Ax.Services.AifMessageInspector.AfterReceiveRequest(Message& request, IClientChannel channel, InstanceContext instanceContext)
貌似是什么权限类的错误,google发现不少人都遇到这个错误(http://community.dynamics.com/product/ax/f/33/p/64670/158445.aspx#158445),有人说要把report service的账号添加到windows access authorized group和pre-windows 2000 compatiable group,还有人说要在ax client configuration中刷新下配置,不知道什么时候还出现了这样的错误:
Unable to write the generated WCF configuration to local storage. The generated WCF configuration will be used from memory. The contents of the new configuration are written to the following temp file: C:\Users\AOS\AppData\Local\Temp\Microsoft.Dynamics.AX.Framework.Services.Client.Configuration.ClientConfigurationInternal.log.
AOS是我的AOS service的账户,也是SQL report service的账户,好像是要被配置文件写到注册表被拒绝而使用内存中的配置信息,不知道是不是这个原因。还有人说升级了AX kernel,错误消失了,就这样放着吧,以后再说,休息一下,休息一下。
这个Report data method可以用在报表的表达式中,更多内容参见http://msdn.microsoft.com/en-us/library/cc587341.aspx