Microsoft Dynamics Error: Developing SSRS report using Report Data Provider - dynamics-crm-2013

I am trying to create a class coding in AOT in my Microsoft Dynamics AX. What I am doing is to develop a SSRS report in Microsoft DynamicsAX 2012. Hence, for practice purposes I am actually following through this tutorial link: http://www.dynamics101.com/2013/09/developing-ssrs-report-using-report-data-provider-microsoft-dynamics-ax-2012/. Please Help. THank you
However, I am getting a syntax error that says:
Syntax error
\Classes\CustReportRDPDemoDP\classDeclaration
classDeclaration
Err:9999
The error occurs at this code line:
[SRSReportDataSetAttribute(tablestr('CustReportRDPDemoTmp'))]
My following code is as follows:
class CustReportRDPDemoDP extends SRSReportDataProviderBase
{
//Temproaray table buffer
CustReportRDPDemoTmp custReportRDPDemoTmp;
[SRSReportDataSetAttribute(tablestr('CustReportRDPDemoTmp'))]
public CustReportRDPDemoTmp getCustReportRDPDemoTmp()
{
select * from custReportRDPDemoTmp;
//return the buffer
return custReportRDPDemoTmp;
}
///<summary>
/// Processes the SQL Server Reporting Services report business logic
/// </summary>
/// <remarks>
/// This method provides the ability to write the report business logic. This method will be called by
/// SSRS at runtime. The method should compute data and populate the data tables that will be returned
/// to SSRS.
/// </remarks>
public void processReport(){
CustTable custTable;
SalesTable salesTable;
//select all customers
while select * from custTable
{
//clear the temporary table
custReportRDPDemoTmp.clear();
//assign customer account and name
custReportRDPDemoTmp.CustAccount = custTable.AccountNum;
custReportRDPDemoTmp.Name = custTable.name();
//select count of invoiced sales order of customer
select count(RecId) from salesTable
where salesTable.CustAccount == custTable.AccountNum
&& salesTable.SalesStatus == SalesStatus::Invoiced;
custReportRDPDemoTmp.SalesOrderInvoiceCount = int642int(salesTable.RecId);
//insert in temporary table buffer
custReportRDPDemoTmp.insert();
}
}
}

You need to have 3 separate methods, you can't just throw the code in the classDeclaration method.
The classDeclaration should just be:
class CustReportRDPDemoDP extends SRSReportDataProviderBase
{
//Temporary table buffer
CustReportRDPDemoTmp custReportRDPDemoTmp;
}
and the two other code blocks should be their own methods.

Related

How to apply a SQL statement on one or more DataTable's?

Question
Please help me writing a C# or vb.net routine that will accept
a DataTable
a SQL query as a string
and create
a DataTable with the result of the query applied to the in put DataTable
To my knowledge, the tool to run SQL in .net is linq, but this does not lead me to a sollution.
In VB.net terms: How do I implement a function like this
Public Function SelectFromDataTable(Sql As String, T1 As DataTable) As DataTable
// Apply Sql to T1
End Function
(or even better, like this)
Public Function SelectFromDataTable(Sql As String, T1 As DataTable, Optional T2 As DataTable) As DataTable
// Apply Sql to T1 and T2
End Function
What I tried so far
For some reason, I thought linq could be the solution, but that is no requirement.
Trial 1
if I look for the combination of linq and DataTable's I get that typicle syntax in which you write sql-like code inline in your .net code, as on. Queries in LINQ to DataSet
I want the query to be defined outside my routine,so can you also create such queries from a SQL string?
Trial 2
Looking for the combination of linq and SQL, I get examples using a SqlDataAdapter, but they need a SqlConnection, which apparently must point to a database, as in How to receive a SQL-Statement as a DataTable
However, for me, not only the destination, but also the source should be a DataTable, so can you also create a SqlConnection to DataTables?
Context
If you are curious where my question comes from:
BluePrism is a graphic Robotic Process Automation (RPA) tool. It has one container object, called a collection, which is under the hood a .net DataTable and gives verry little support to manipulate these.
Fortunately, one can create so called "business objects" in .net and implements "Action" that receive and return variables. (This is meant to manipulate other applications, but can also be used to manipulate data.)
We already have such an object, which we called Collection Manipulation. One of the actions, Filter Collection, is implemented as
Dim NewRow As DataRow
Collection_Out = Collection_In.Clone
For Each parentRow As DataRow In Collection_In.Select(Select_Condition)
NewRow = Collection_Out.NewRow
For Each c As DataColumn In NewRow.Table.Columns
NewRow(c.ColumnName) = parentRow(c.ColumnName)
Next
Collection_Out.Rows.Add(NewRow)
Next
NewRow = Nothing
Collection_In = Nothing
I would like to implement a general purpose Action, to runs queries against my collection like
select category, sum(unit_price * units) as total_price
from invoice
group by category;
select article, order.units - delivery.units as units_missing
from order, delivery
where order.article = delivery.article;
If I understood your question correct, you want a SQL syntax like multi-purpose select for data tables.
Based on the info found here: https://www.hanselman.com/blog/TheWeeklySourceCode48DynamicQueryableMakesCustomLINQExpressionsEasier.aspx, I wrote the following example further down. You can expand it as you see fit.
TL;DR Add the System.Linq.Dynamic NuGet package so you can use strings for where clauses amongst others.
BTW: Writing a query string parser, to parse for instance "select category, sum(unit_price * units) as total_price from invoice group by category;" is entirely possible, but IMHO you will spend a lot of time to gain little.
using System.Data;
using System.Linq;
using System.Linq.Dynamic;
namespace Foo {
public class Bar {
/// <summary>
///
/// </summary>
/// <param name="from"></param>
/// <param name="where"></param>
/// <param name="skipRows"></param>
/// <param name="takeRows"></param>
/// <param name="orderBy">Needed for range selections (skipRows, takeRows) </param>
/// <returns></returns>
public DataTable GeneralPurposeSelect(DataTable from, string where = null, int? skipRows = null, int? takeRows = null, string orderBy = "Id") {
var fromQryAble = from.AsEnumerable().AsQueryable();
IQueryable<DataRow> toQryAble = null;
if (!string.IsNullOrEmpty(where)) {
toQryAble = fromQryAble.Where(where);
}
if (takeRows != null) {
if (skipRows == null) {
skipRows = 0;
}
}
if (skipRows != 0) {
if (takeRows == null) {
takeRows = int.MaxValue;
}
}
if (takeRows != null) {
if (skipRows == null) {
skipRows = 0;
}
toQryAble = toQryAble == null ?
fromQryAble.OrderBy(orderBy).Skip(skipRows.Value).Take(takeRows.Value) :
toQryAble.OrderBy(orderBy).Skip(skipRows.Value).Take(takeRows.Value);
}
return toQryAble == null ? from : toQryAble.CopyToDataTable();
}
}
}

How do I add a lazy loaded column in EntitySpaces?

If you do not have experience with or aren't currently using EntitySpaces ("ES") ORM this question is not meant for you.
I have a 10 year old application that after 4 years now needs my attention. My application uses a now defunct ORM called EntitySpaces and I'm hoping if you're reading this you have experience or maybe still use it too! Switching to another ORM is not an option at this time so I need to find a way to make this work.
Between the time I last actively worked on my application and now (ES Version 2012-09-30), EntitySpaces ("ES") has gone through a significant change in the underlying ADO.net back-end. The scenario that I'm seeking help on is when an entity collection is loaded with only a subset of the columns:
_products = new ProductCollection();
_products.Query.SelectAllExcept(_products.Query.ImageData);
_products.LoadAll();
I then override the properties that weren't loaded in the initial select so that I may lazyload them in the accessor. Here is an example of one such lazy-loaded property that used to work perfectly.
public override byte[] ImageData
{
get
{
bool rowIsDirty = base.es.RowState != DataRowState.Unchanged;
// Check if we have loaded the blob data
if(base.Row.Table != null && base.Row.Table.Columns.Contains(ProductMetadata.ColumnNames.ImageData) == false)
{
// add the column before we can save data to the entity
this.Row.Table.Columns.Add(ProductMetadata.ColumnNames.ImageData, typeof(byte[]));
}
if(base.Row[ProductMetadata.ColumnNames.ImageData] is System.DBNull)
{
// Need to load the data
Product product = new Product();
product.Query.Select(product.Query.ImageData).Where(product.Query.ProductID == base.ProductID);
if(product.Query.Load())
{
if (product.Row[ProductMetadata.ColumnNames.ImageData] is System.DBNull == false)
{
base.ImageData = product.ImageData;
if (rowIsDirty == false)
{
base.AcceptChanges();
}
}
}
}
return base.ImageData;
}
set
{
base.ImageData = value;
}
}
The interesting part is where I add the column to the underlying DataTable DataColumn collection:
this.Row.Table.Columns.Add(ProductMetadata.ColumnNames.ImageData, typeof(byte[]));
I had to comment out all the ADO.net related stuff from that accessor when I updated to the current (and open source) edition of ES (version 2012-09-30). That means that the "ImageData" column isn't properly configured and when I change it's data and attempt to save the entity I receive the following error:
Column 'ImageData' does not belong to table .
I've spent a few days looking through the ES source and experimenting and it appears that they no longer use a DataTable to back the entities, but instead are using a 'esSmartDictionary'.
My question is: Is there a known, supported way to accomplish the same lazy loaded behavior that used to work in the new version of ES? Where I can update a property (i.e. column) that wasn't included in the initial select by telling the ORM to add it to the entity backing store?
After analyzing how ES constructs the DataTable that is uses for updates it became clear that columns not included in the initial select (i.e. load) operation needed to be added to the esEntityCollectionBase.SelectedColumns dictionary. I added the following method to handle this.
/// <summary>
/// Appends the specified column to the SelectedColumns dictionary. The selected columns collection is
/// important as it serves as the basis for DataTable creation when updating an entity collection. If you've
/// lazy loaded a column (i.e. it wasn't included in the initial select) it will not be automatically
/// included in the selected columns collection. If you want to update the collection including the lazy
/// loaded column you need to use this method to add the column to the Select Columns list.
/// </summary>
/// <param name="columnName">The lazy loaded column name. Note: Use the {yourentityname}Metadata.ColumnNames
/// class to access the column names.</param>
public void AddLazyLoadedColumn(string columnName)
{
if(this.selectedColumns == null)
{
throw new Exception(
"You can only append a lazy-loaded Column to a partially selected entity collection");
}
if (this.selectedColumns.ContainsKey(columnName))
{
return;
}
else
{
// Using the count because I can't determine what the value is supposed to be or how it's used. From
// I can tell it's just the number of the column as it was selected: if 8 colums were selected the
// value would be 1 through 8 - ??
int columnValue = selectedColumns.Count;
this.selectedColumns.Add(columnName, columnValue);
}
}
You would use this method like this:
public override System.Byte[] ImageData
{
get
{
var collection = this.GetCollection();
if(collection != null)
{
collection.AddLazyLoadedColumn(ProductMetadata.ColumnNames.ImageData);
}
...
It's a shame that nobody is interested in the open source EntitySpaces. I'd be happy to work on it if I thought it had a future, but it doesn't appear so. :(
I'm still interested in any other approaches or insight from other users.

LightSwitch - bulk-loading all requests into one using a domain service

I need to group some data from a SQL Server database and since LightSwitch doesn't support that out-of-the-box I use a Domain Service according to Eric Erhardt's guide.
However my table contains several foreign keys and of course I want the correct related data to be shown in the table (just doing like in the guide will only make the key values show). I solved this by adding a Relationship to my newly created Entity like this:
And my Domain Service class looks like this:
public class AzureDbTestReportData : DomainService
{
private CountryLawDataDataObjectContext context;
public CountryLawDataDataObjectContext Context
{
get
{
if (this.context == null)
{
EntityConnectionStringBuilder builder = new EntityConnectionStringBuilder();
builder.Metadata =
"res://*/CountryLawDataData.csdl|res://*/CountryLawDataData.ssdl|res://*/CountryLawDataData.msl";
builder.Provider = "System.Data.SqlClient";
builder.ProviderConnectionString =
WebConfigurationManager.ConnectionStrings["CountryLawDataData"].ConnectionString;
this.context = new CountryLawDataDataObjectContext(builder.ConnectionString);
}
return this.context;
}
}
/// <summary>
/// Override the Count method in order for paging to work correctly
/// </summary>
protected override int Count<T>(IQueryable<T> query)
{
return query.Count();
}
[Query(IsDefault = true)]
public IQueryable<RuleEntryTest> GetRuleEntryTest()
{
return this.Context.RuleEntries
.Select(g =>
new RuleEntryTest()
{
Id = g.Id,
Country = g.Country,
BaseField = g.BaseField
});
}
}
public class RuleEntryTest
{
[Key]
public int Id { get; set; }
public string Country { get; set; }
public int BaseField { get; set; }
}
}
It works and all that, both the Country name and the Basefield loads with Autocomplete-boxes as it should, but it takes VERY long time. With two columns it takes 5-10 seconds to load one page.. and I have 10 more columns I haven't implemented yet.
The reason it takes so long time is because each related data (each Country and BaseField) requires one request. Loading a page looks like this in Fiddler:
This isn't acceptable at all, it should be a way of combining all those calls into one, just as it does when loading the same table without going through the Domain Service.
So.. that was a lot explaining, my question is: Is there any way I can make all related data load at once or improve the performance by any other way? It should not take 10+ seconds to load a screen.
Thanks for any help or input!s
My RIA Service queries are extremely fast, compared to not using them, even when I'm doing aggregation. It might be the fact that you're using "virtual relationships" (which you can tell by the dotted lines between the tables), that you've created using your RuleEntryTest entity.
Why is your original RuleEntry entity not related to both Country & BaseUnit in LightSwitch BEFORE you start creating your RIA entity?
I haven't used Fiddler to see what's happening, but I'd try creating "real" relationships, instead of "virtual" ones, & see if that helps your RIA entity's performance.

Get serialization error when try and submit EF4 entity via WCF

I have an entity CustomerActivityReport which I'm trying to submit to the server via WCF. On the server end I'm using the repository + UOW patterns to update/insert the entity into the db.
CustomerActivityReport has a many to many relationship to another entity LookupValue. When I try and submit an instance of CustomerActivityReport, the DataContractSerializer throws the error: "Object graph for type 'FixupCollection[CustomerActivityReport]' contains cycles and cannot be serialized if reference tracking is disabled". I am getting this error even when I don't set the relationship on the LookupValue entities.
To get around this I've tried applying [DataContract(IsReference = true)] to both the entities in question and also to FixupCollection. But then I get different problems.
Has anybody else run into similar problems when trying to submit related entities over WCF?
Thanks in advance for any replies.
Ryan
The times that we have had a similar problem we were missing an attribute on a sub object.
I couldn't get this working with FixupCollection, and so I've had to submit all my entity collections as standard Collection, and then add logic server end to change them back into FixupCollection.
Client:
convertedCustomerActivityReport.LookupValues = new Collection<LookupValue>()
Server:
public virtual ICollection<LookupValue> LookupValues
{
get
{
if (_lookupValues == null || _lookupValues is Array)
{
var newCollection = new FixupCollection<LookupValue>();
newCollection.CollectionChanged += FixupLookupValues;
newCollection.AddRange(_lookupValues);
_lookupValues = newCollection;
}
return _lookupValues;
}
I've also added an AddRange method to FixupCollection:
/// <summary>
/// Adds multiple items.
/// </summary>
/// <param name="items">The items to add.</param>
public void AddRange(IEnumerable<T> items)
{
if (items == null)
{
return;
}
foreach (var item in items)
{
this.Add(item);
}
}

Access SQL Server 2008 Change Tracking Info via Entity Framework

Based on this link it looks like I can get date inserted / date modified information "for free" (without the need for triggers etc.) using Sql Server 2008 by simply enabling Change Tracking. I am using Entity Framework to access the data. My question is, how do I access date modified / date inserted information for the database records via Entity Framework and LINQ?
I'm using VS2008 so I don't have the new EF v4 stuff yet (but if it can be done in the new EF please let me know).
Thanks.
Aw, nevermind. I found it plenty easy to just create the Inserted and LastModified columns in each table myself, set a default value for each, and build an update trigger for LastModified. That seems to be what people do, and the Change Tracking looks like it's mainly set up for sync'ing. Right?
OK, this works beautifully (also see this article)...
public partial class MyEntities
{
partial void OnContextCreated()
{
this.SavingChanges += new System.EventHandler(CustomSavingChangesLogic);
}
/// <summary>
/// Apply timestamps
/// </summary>
public void CustomSavingChangesLogic(object sender, System.EventArgs e)
{
var changedEntities = ((ObjectContext)sender).ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified);
foreach (var stateEntryEntity in changedEntities)
{
if(!stateEntryEntity.IsRelationship) {
var entity = stateEntryEntity.Entity;
var lastModifiedPropInfo = entity.GetType().GetProperty("LastModified");
if (lastModifiedPropInfo != null)
lastModifiedPropInfo.SetValue(entity, DateTime.UtcNow, null);
if (stateEntryEntity.State == EntityState.Added)
{
var createdPropInfo = entity.GetType().GetProperty("Created");
if (createdPropInfo != null)
createdPropInfo.SetValue(entity, DateTime.UtcNow, null);
}
}
}
}
}