Using RDLC subreport multiple times - c#

I have a report that lists off taxes for a store given a storeID, a fromDate, and a toDate. I want to use this report as a subreport multiple times in the same parent report. For example, if you use storeID's 1,2,3,4,5 you would have this subreport show 5 times, with the first one having data for store 1, second one having data for store 2, etc.
Is this even possible? I've found some forums where people are asking how to use the same subreport twice in a report, but the threads always die out before they find something. I also saw you can use parameters but I'm not sure how those work (a bit new to sub-reporting in RDLC).
Thanks!

If I understand correctly, probably you can use a single SubReport containing a Table displaying taxes grouped by store.

In the main report, use 5 instances of the subreport.
Change the (Name) property of each subreport, but keep the subreport path the same (as you already did).
When processing the main report, the method LocalReport_SubReportProcessing called many times according to instances count of the subreport found.
You'll make a global variable keeps the current store Id as in the code sampel below.
public class TaxDetailsOfStore
{
public int StoreId { get; set; }
// Your other details here...
}
// This global property be filled for all stores before displaying the main report.
private List<TaxDetailsOfStore> TaxDetailsOfAllStores { get; set; }
private int _storeId = 1; // To start with.
private const int _maxStoreId = 5;
private void LocalReport_SubReportProcessing(object sender, SubreportProcessingEventArgs e)
{
string reportDataSourceName = string.Empty;
object reportDataSourceValue = null;
switch (e.ReportPath)
{
case "SubRprtTaxDetailsOfStore":
int storeId = Convert.ToInt32(e.Parameters["storeId"].Values[0]);
var lstTaxDetailsOfStore = TaxDetailsOfAllStores.FindAll(x => x.StoreId == storeId);
//-------------------------------------------------------------------------------------
// Increment the variable _storeId till reaches _maxStoreId and then back again from 1.
_storeId = _storeId++ % _maxStoreId;
//-------------------------------------------------------------------------------------
reportDataSourceName = "TaxDetailsOfStoreDataSet";
reportDataSourceValue = lstTaxDetailsOfStore;
break;
// Handle other sub reports like this.
//case "SubRprtAnother1":
// object dsSubRprtAnother1 = null;
// reportDataSourceName = "AnotherDataSetName1";
// reportDataSourceValue = dsSubRprtAnother1;
// break;
//case "SubRprtSummary":
// object dsSubRprtSummary = null;
// reportDataSourceName = "SubRprtSummaryDataSet";
// reportDataSourceValue = dsSubRprtSummary;
// break;
}
ReportDataSource reportDataSource = new ReportDataSource
{
Name = reportDataSourceName,
Value = reportDataSourceValue
};
e.DataSources.Add(reportDataSource);
}

Related

Get field data outside of reporting database using Encompass360 SDK

I'm trying to build a standalone application that creates a custom report for Encompass360 without needing to put certain fields into the reporting database.
So far I have only found one way to do it, but it is extremely slow. (Much slower than a normal report within encompass when retrieving data outside of the reporting database.) It takes almost 2 minutes to pull the data for 5 loans doing this:
int count = 5;
StringList fields = new StringList();
fields.Add("Fields.317");
fields.Add("Fields.3238");
fields.Add("Fields.313");
fields.Add("Fields.319");
fields.Add("Fields.2");
// lstLoans.Items contains the string location of the loans(i.e. "My Pipeline\Dave#6")
foreach (LoanIdentity loanID in lstLoans.Items)
{
string[] loanIdentifier = loanID.ToString().Split('\\');
Loan loan = Globals.Session.Loans.Folders[loanIdentifier[0]].OpenLoan(loanIdentifier[1]);
bool fundingPlus = true; // if milestone == funding || shipping || suspended || completion;
if (!fundingPlus)
continue;
bool oneIsChecked = false;
LogMilestoneEvents msEvents = loan.Log.MilestoneEvents;
DateTime date;
MilestoneEvent ms = null; // better way to do this probably
if (checkBox4.Checked)
{
ms = msEvents.GetEventForMilestone("Completion");
if (ms.Completed)
{
oneIsChecked = true;
}
}
else if (checkBox3.Checked)
{
ms = msEvents.GetEventForMilestone("Suspended");
if (ms.Completed)
{
oneIsChecked = true;
}
}
else if (checkBox2.Checked)
{
ms = msEvents.GetEventForMilestone("Shipping");
if (ms.Completed)
{
oneIsChecked = true;
}
}
else if (checkBox1.Checked)
{
ms = msEvents.GetEventForMilestone("Funding");
if (ms.Completed)
{
oneIsChecked = true;
}
}
if (!oneIsChecked)
continue;
string LO = loan.Fields["317"].FormattedValue;
string LOid = loan.Fields["3238"].FormattedValue;
string city = loan.Fields["313"].FormattedValue;
string address = loan.Fields["319"].FormattedValue;
string loanAmount = loan.Fields["2"].FormattedValue;
if (loanAmount == "")
{
Console.WriteLine(LO);
continue;
}
int numLoans = 1;
addLoanFieldToListView(LO, numLoans, city, address, loanAmount);
if (--count == 0)
break;
}
}
I haven't been able to figure out how to use any of the pipeline methods to retrieve data outside the reporting database, but when all of the fields I am looking for are in the reporting database, it hardly takes a couple seconds to retrieve the contents of hundreds of loans using these tools:
session.Reports.SelectReportingFieldsForLoans(loanGUIDs, fields);
session.Loans.QueryPipeline(selectedDate, PipelineSortOrder.None);
session.Loans.OpenPipeline(PipelineSortOrder.None);
What would really help me is if somebody provided a simple example for retrieving data outside of the reporting database by using the encompass sdk that doesn't take longer than it ought to for retrieving the data.
Note: I am aware I can add the fields to the reporting database that aren't in it currently, so this is not the answer I am looking for.
Note #2: Encompass360 doesn't have it's own tag, if somebody knows of better tags that can be added for the subject at hand, please add them.
I use the SelectFields method on Loans to retrieve loan field data that is not in the reporting database in Encompass. It is very performant compared to opening loans up one by one but the results are returned as strings so it requires some parsing to get the values in their native types. Below is the example from the documentation for using this method.
using System;
using System.IO;
using EllieMae.Encompass.Client;
using EllieMae.Encompass.BusinessObjects;
using EllieMae.Encompass.Query;
using EllieMae.Encompass.Collections;
using EllieMae.Encompass.BusinessObjects.Loans;
class LoanReader
{
public static void Main()
{
// Open the session to the remote server
Session session = new Session();
session.Start("myserver", "mary", "maryspwd");
// Build the query criterion for all loans that were opened this year
DateFieldCriterion dateCri = new DateFieldCriterion();
dateCri.FieldName = "Loan.DateFileOpened";
dateCri.Value = DateTime.Now;
dateCri.Precision = DateFieldMatchPrecision.Year;
// Perform the query to get the IDs of the loans
LoanIdentityList ids = session.Loans.Query(dateCri);
// Create a list of the specific fields we want to print from each loan.
// In this case, we'll select the Loan Amount and Interest Rate.
StringList fieldIds = new StringList();
fieldIds.Add("2"); // Loan Amount
fieldIds.Add("3"); // Rate
// For each loan, select the desired fields
foreach (LoanIdentity id in ids)
{
// Select the field values for the current loan
StringList fieldValues = session.Loans.SelectFields(id.Guid, fieldIds);
// Print out the returned values
Console.WriteLine("Fields for loan " + id.ToString());
Console.WriteLine("Amount: " + fieldValues[0]);
Console.WriteLine("Rate: " + fieldValues[1]);
}
// End the session to gracefully disconnect from the server
session.End();
}
}
You will highly benefit from adding these fields to the reporting DB and using RDB query instead. Internally, Encompass has to open / parse files when you read fields without RDB, which is a slow process. Yet it just does a SELECT query on fields in RDB which is a very fast process. This tool will allow you quickly checking / finding which fields are in RDB so that you can create a plan for your query as well as a plan to update RDB: https://www.encompdev.com/Products/FieldExplorer
You query RDB via Session.Loans.QueryPipeline() very similarly to your use of Loan Query. Here's a good example of source code (in VB): https://www.encompdev.com/Products/AlertCounterFieldPlugin

Passing ReportParameters back to method

I have an application that is doing all of our reporting with locally stored RDLs. I have two forms, one is a simple viewer that contains a TabPageControl, and the other is an FLP of controls that the user can use to select report parameters. We are planning on having over 30+ options, but at the moment we only have around 5 (certain ones display for certain reports, the rest are hidden).
The issue I'm running into is that I need to have some way to get the options they chose out of a List or Collection. The method I'm using right now is
public void AddReportToViewer(string reportName, List<ReportParameter> parameterList = null)
{
TabPage newPage = new TabPage();
ReportViewer reportViewer = new ReportViewer();
newPage.Text = reportName;
newPage.Controls.Add(reportViewer);
tbcReports.TabPages.Add(newPage); //adding the report viewer to page, should do this at the end of the method
reportViewer.Reset();
reportViewer.ProcessingMode = ProcessingMode.Local;
reportViewer.LocalReport.ReportPath = "MyPath/reportName";
ReportDataSource rds = new ReportDataSource();
//parse parameters to individual objects to use with method calls
int parameterEmployeeID = 0;
int parameterStoreID = 0;
List<int> parameterStoreIDs = null;
DateTime parameterFromDate = DateTime.Now;
DateTime parameterToDate = DateTime.Now;
//Use parameters to get objects, just one here, but we have multiple
List<EmployeeTimeclockReportObjects> timeclock = reportBL.getEmployeeTimeclock(parameterEmployeeID, parameterStoreID, parameterFromDate, parameterToDate);
reportBindingSource.DataSource = timeclock;
rds = new ReportDataSource("TimeclockEntries", reportBindingSource);
reportViewer.LocalReport.DataSources.Add(rds);
reportViewer.RefreshReport();
reportViewer.Dock = DockStyle.Fill;
}
This all works, but here's where I need help
int parameterEmployeeID = 0;
int parameterStoreID = 0;
List<int> parameterStoreIDs = null;
DateTime parameterFromDate = DateTime.Now;
DateTime parameterToDate = DateTime.Now;
I need to strip the values from the list of report parameters so I can use them in the data population methods. For example, I'm doing something like...
int parameterEmployeeID = Convert.ToInt32(parameterList[parameterList.FindIndex(x => x.Name == "employeeID")].Values[0]);
But then I run into issues on the List<int> parameterStoreIDs, because I can't convert a StringCollection to a List of ints.
Does anyone have any recommendations on either a) converting the StringCollection to a list of ints, or b) anything else I could try doing to get the information back from the second form besides a List<ReportParameters> ???
For question (a), consider something like this:
I am not clear on what variable in the code would be the StringCollection, so for the sake of the example I'm just calling it strings.
List<int> ints = strings.Cast<string>(s => Convert.ToInt32(s)).ToList();

How to display data from mysql and display it one by one?

I have a data in mysql and I want to display the data one by one everytime I click the button. How to do it?
string ConnectToServer = #"server=..*.;port=****; user id=sampleID; password=samplePW; database=sampleDB; pooling=false";
public void GetNames()
{
MySqlConnection NameConnector = null;
MySqlDataReader NameReader = null;
try
{
NameConnector = new MySqlConnection(ConnectToServer);
NameConnector.Open();
string Name = "SELECT * from sampleNames";
MySqlCommand NameCommand = new MySqlCommand(Name, NameConnector);
NameReader = NameCommand.ExecuteReader();
while (NameReader.Read())
{
Console.WriteLine(NameReader.GetInt32(0) + ": " + NameReader.GetString(1));
NameLabel.Text += NameReader.GetString("Names") + "\n";
}
}
catch (MySqlException NameException)
{
Console.WriteLine("error : (0)", NameException.ToString());
}
finally
{
if (NameReader != null)
{
NameReader.Close();
}
if (NameConnector != null)
{
NameConnector.Close();
}
}
}
private void ButtonName_Click(object sender, EventArgs e)
{
GetNames();
}
the output:
Name1
Name2
Name3
Name4
Name5
but I wan't is, the Name will appear one by one each time I click the button
like this:
click = output Name1
click = output Name2
click = output Name3
click = output Name4
click = output Name5
There are at least 2 ways of doing that depending on how real-time you need the data and how many DB calls do you want to make. here they are:
Option #1
Initialize a class level variable for names list and an index variable.
List<string> names = null;
int currentNameIndex = 0;
on the click handler, if names is null, populate the names variable with all names in the DB. display the first item as follows.
private void ButtonName_Click(object sender, EventArgs e)
{
if (names == null)
{
names = GetNames();
}
if (currentNameIndex < names.Count)
{
NameLabel.Text += names[currentNameIndex++];
}
}
the getnames need to be modified to return the list of names.
Option #2
Instead of retrieving the whole list in 1 DB call, you could change the SQL query to get the first record from the Table. (based on a Id or some key)
On a click, GetNames will retrieve only 1 record and display that.
On the next click it'll retrieve another record, but not the first ones.
This would typically involve a query involving a key column. Please post your table schema and I can answer with the Query.
an e.g. Query is
int currentNameId = -1; // class level variable.
query is
Select TOP 1 nameId, names from SampleNames Where NameId > currentNameId Order By NameId;
currentNameId = int.Parse(NameReader[nameId].ToString());
the above query assumes that nameId is a unique key and that values start from 0 or greater than -1, and that they are incremental. (identity PKs etc.)
as I mentioned, if you can provide the table structure, we can answer better.
Option #1 is efficient in DB calls but may potentially have stale data.
Option #2 is more chatty but has more real-time data than Option #1.
You are reading all records:
while (NameReader.Read())
If you want to read just one, try put all your connection outside the method and run
NameReader = NameCommand.ExecuteReader();
only once.
Then change
while (NameReader.Read())
to
NameReader.Read()

C#: recursive search in two different data structures

I need to perform a search in two different data structures in C#, and here's the deal:
I have one name (which is a string) and I want to perform a search. I have a function called Exists which will return a bool indicating whether it exists or not.
In case it exists, I increase the name (simply adding a 1 at the end of the string), and then I need to perform the search again (via method exists) to see if an object with the new name exists.
This would go on until there's an unused name, which I could use, BUT, in case it doesn't exist, now I should perform a search another data structure which contains the objects that were deleted, and if the string is found there, then I'd have to increase the name again, and start searching since the beginning.
This would all end in case there's no object with such name neither using Exists method nor in the data structure where all the deleted objects are.
How could I approach this problem?
I hope I expressed myself clearly :-)
Thanks a lot in advance!
string BuildNextName(string originalName)
{
string name = originalName;
while( Exists(name) || deletedNames.Contains(name))
{
name = Increment(name);
}
return name;
}
Or did I miss something?
Using a for loop:
string BuildNextName(string originalName)
{
for (string name=originalName;
Exists(name) || deletedNames.Contains(name);
name = Increment(name));
return name;
}
BTW, I guess your name incrementation algorithm is more complex than simply adding 1: name, name1, name2,... Basically, if the name doesn't end in a number, you append "1". If it does, you increment that number. right?
a non recursive and simple solution could be something like this ( I don't see any need of recursion in this case)
//pseudocode
String name;
bool condition = true;
while(condition)
{
if(ExistInFirstDataStructure(name))
{
//increment name
}
else
{
if(ExistInDeletedDataStructure(String name))
{
//increment name
}
else
{
condition = false;
}
}
}
bool ExistInFirstDataStructure(String name)
{
}
bool ExistInDeletedDataStructure(String name)
{
}
Why use a loop at all?? (I know LINQ will under the hood)
var LastUsedObjectName =
MyObjects.Select(mo => mo.Name)
.Union( MyDeletedObjects.Select(mo => mo.Name))
.OrderByDescending(name => /*Function to order by integer part of name*/).First();
// Now add 1 to LastUseObjectName and use that.
How about this one:
var listOfExistingNames = new List<string> { "MyName", "MyName1", "MyName3" };
var listOfDeletedNames = new List<string> { "MyName2", "MyName5" };
int counter = 0;
string baseToFindFreePlace = "MyName";
string newName = baseToFindFreePlace;
var allNames = listOfExistingNames.Concat(listOfDeletedNames);
while (allNames.Contains(newName))
{
counter++;
newName = baseToFindFreePlace + counter;
}
listOfExistingNames.Add(newName);
if you create Exists methods for both data structures, you can search with recursion like this:
pseudo code:
string resultName;
void Search(string name)
{
if(ExistsInFirstStructure(name)) //name is in first data structure
Search(name + "1"); //add 1 and try again
else
if(ExistsInSecondStructure(name)) //name exists in second data structure
Search(name + "1"); //perform search again
else
resultName = name; //current name wasn't found in first and second data structures - we have result
}

Need help in using MySqlTransaction

Hi all i would like to use MySqlTransaction in my requirement. Actually i am having a doubt regarding that i.e as per my requirement i will have to delete different values from database.
The process i am doing is as follows. Assume that i am having 2 EmpIDs where this EmpID will hold different values which may be multiple. I will store the corresponding values for that particular EmpID using Dictionary and then i will save them to a list corresponding to the EmpID.
Assume that i am having list element as follows
For EmpID 1 i will have 1,2. I will check for the maximum value from the datbase in this list if exists i would like to delete this EmpID from the database.
For EmpID 2 i will have 1,2. But in my database i will have 3 as maximum values. So this one fails . I would like to rollback the previously deleted item .
Is it possible to do with a transaction if so can any one help me in solving this
Sample i code
if(findMax(lst,iEmpID)
{
obj.delete("storeprocname"); // this will occur when my list has maximum value
}
else
{
//Here i would like to rollback my previous one referring to the delete method in class file
}
My sample code
if (findMaxPayPeriodID(lstPayPeriodID, iEmpIDs)) //Assume for the first time maxpayperiod exists and for the second time it fails how to rollback then
{
if (findSequence(lstPayPeriodID)) // Assume this is also true for first time
{
for (int ilstPayperiodID = 0; ilstPayperiodID < lstPayPeriodID1.Count; ilstPayperiodID++)
{
oAdmin.Payperiodnumber = (int)lstPayPeriodID1[ilstPayperiodID];
for (int ilistPayYear = iPayYearcnt; ilistPayYear < lstPayYear1.Count; ilistPayYear++)
{
oAdmin.PayYear = (int)lstPayYear1[ilistPayYear];
iPayYearcnt++;
break;
}
for (int ilistDateTime = idtcnt; ilistDateTime < lstDateTime1.Count; ilistDateTime++)
{
idtcnt++;
oAdmin.PaymentDate = lstDateTime1[ilistDateTime];
break;
}
}
if (oAdmin.deletePayRoll(oSqlTran))
{
oMsg.Message = "Deleted Sucessfully";
oMsg.AlertMessageBox(out m_locallblMessage);
Page.Controls.Add(m_locallblMessage);
oAdmin.FedTaxID = ddlFedTaxID.SelectedValue;
oAdmin.PayFrequency = ddlPaymentType.SelectedValue.ToString();
mlocal_strStoredProcName = "uspSearchPayRoll";
oAdmin.getPayRollDetails(out mlocal_ds, mlocal_strStoredProcName);
//grdPayroll.Visible = true;
grdPayroll.DataSource = mlocal_ds;
grdPayroll.DataBind();
if (mlocal_ds != null)
{
btnDelete.Visible = true;
}
else
btnDelete.Visible = false;
}
lstPayPeriodID.Clear();
lstDateTime.Clear();
lstPayYear.Clear();
iPayIDcnt = 0;
iPayYearcnt = 0;
idtcnt = 0;
}
else
{
rollback should be done
}
You don't provide enough information - esp. since it seems that you will use a Stored Procedure for the delete operation all bets are off...
The only option I can think of is to make sure that you find first the maximum EmpId not from one list BUT from all lists first... then just check that against the DB and act accordingly...
This way the DB will only be hit twice (for the check and for the delete/Stored Procedure)... which is definetely better in terms of scaling etc.

Categories

Resources