Microsoft Reporting with Collection as DataSource - reporting-services

I'm working with MS Reporting Services. The underlying datasource is
IEnumerable<MyObject>, I'm not using DataSets.
Every MyObject has properties and some other IEnumerable collections.
In the report I want to display all the properties from MyObject and
the collections lists too.
I didn't know how to display this inner collections, so I've made a SubReport to which I passed the MyObject.Id so that the SubReport could retrieve the object by himself and Build a the DataSource for these inner collections.
I do this in this event.
myReportViewer.LocalReport.SubreportProcessing += new SubreportProcessingEventHandler(LocalReport_SubreportProcessing);
private void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e)
{
int id;
if (e.Parameters.Count > 0 && int.TryParse(e.Parameters[0].Values[0], out id))
{
MyObject current = myObjects.Where(x => x.MyObject.Id == id).FirstOrDefault();
InnerListBindingSource.DataSource = current.InnerCollection;
e.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource(
"MyInnerCollectionDataSource", InnerListBindingSource));
}
}
But there is always "The SubReport could not be shown" in my Master Report.
(Master report - subreport are correctly binded)
Any Idea why? Or how to resolve this in a more elegant way ?
Thank you

OK.
So I went to this solution and it's working:
private IEnumerable<MyObject> myObjects;
public ReportViewerForm(IEnumerable<MyObject> myObjects)
{
InitializeComponent();
this.myObjects = myObjects;
this.WindowState = FormWindowState.Maximized;
ReportViewer reportViewer = new ReportViewer();
reportViewer.ProcessingMode = ProcessingMode.Local;
reportViewer.LocalReport.ReportEmbeddedResource = #"SomePath." + "Report1.rdlc";
/*reportViewer.LocalReport.ReportPath = #"SomePath\Report1.rdlc"; */
reportViewer.LocalReport.SubreportProcessing +=
new SubreportProcessingEventHandler(SubreportProcessingEventHandler);
reportViewer.LocalReport.DataSources.Add(
new ReportDataSource("MyDataSource", myObjects));
reportViewer.LocalReport.SetParameters(new List<ReportParameter>
{
new ReportParameter("param1", ..WhatEver..),
...
});
reportViewer.Dock = DockStyle.Fill;
this.panel1.Controls.Add(reportViewer);
reportViewer.RefreshReport();
}
void SubreportProcessingEventHandler(object sender, SubreportProcessingEventArgs e)
{
/* For example ID parsing.. when you have it defined in .rdlc file.. */
int id;
if (e.Parameters.Count > 0 && int.TryParse(e.Parameters[0].Values[0], out id))
{
MyObject current = myObjects.Where(x => x.MyObject.Id == id).FirstOrDefault();
e.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource(
"InnerListDataSource", current.InnerList));
}
}

If I understand you correctly, you have a structure that resembles a table. So why don't you take a DataTable? ReportingServices offers easy access to those.
Or did I get you wrong there?

Related

Display SSRS Report Author

In SSRS there is a report property called Author.
I am attempting to display the author at the bottom of my report in a text box. I tried the following:
="Report Created By " & Globals!Author
But it didn't work. Please let me know what I am missing here.
I do not think this data is exposed in th report builder, however, If you want to maintain a custom assembly and embed it in your report then you can take the code below and add to be called by your report's custom code.
Create class library project that references Microsoft.SqlServer.ReportingServices2010 similar to below.
using RS = Microsoft.SqlServer.ReportingServices2010;
public class MicrosoftReportingService //: IReportService
{
private RS.ReportingService2010 _service=new RS.ReportingService2010();
public void CheckConnection()
{
_service.Url = <SSSR_ENPOINT>;
NetworkCredential credentials = new NetworkCredential(<USER_NAME>,<PASSWORD>,<DOMAIN>);
_service.Credentials=credentials;
}
private List<RS.CatalogItem> GetReportsInFolder(string path)
{
CheckConnection();
RS.CatalogItem[] reportItems = _service.ListChildren(path, true);
List<RS.CatalogItem> result = new List<RS.CatalogItem>();
foreach (RS.CatalogItem reportItem in reportItems)
if (reportItem.TypeName == "Report" || reportItem.TypeName == "LinkedReport")
result.Add(reportItem);
return result;
}
public CatalogItem GetReportData(string reportPath, string reportName)
{
List<RS.CatalogItem> reportItems = GetReportsInFolder(reportPath);
return reportItems.Where(p=>p.Name==reportName).FirstOrDefault();
}
public string GetReportAuthor(string reportPath, string reportName)
{
CatalogItem item = GetReportData(reportPath,reportName);
return (item == null) ? "" : item.CreatedBy;
}
}
Include the assembly in your report.
Once the assembly is on the report server and referenced by your report then you can create a wrapper function in the report custom code similar to:
Public GetReportAuthor(reportPath As String, reportName As String) As String
Dim rs As New MicrosoftReportingService()
Return rs.GetReporAuthor(reportPath, reportName)
End Procedure
Finally, you can embed the code into an expression.
lblAuthor = Code.GetReportAuthor(<REPORT_PATH>,<REPORT_NAME>)

Existing posts keep on re-added into jTable with newer post

Here are my codes :
public void submitReply(ActionEvent e) {
String replyBy = userName;
String reply = jTextArea_reply.getText();
if (reply.equals("")) {
JOptionPane.showMessageDialog(null, "Comment cannot leave blank");
} else {
eForumTopics comment = new eForumTopics(replyBy, reply);
if (comment.createComment() == true) {
JOptionPane
.showMessageDialog(null,
"Reply submitreted successfully. You will be redirect to main page.");
SetUpJTableComment();
public void SetUpJTableComment() {
// Get jTable model
DefaultTableModel tableModel1 = (DefaultTableModel) jTableComment
.getModel();
// Store column data into Array (3 columns)
String[] data = new String[3];
// Set Up Database Source
db.setUp("IT Innovation Project");
String sql = "Select reply_content,reply_by from forumReplies WHERE reply_topic = "
+ topicId + "";
ResultSet resultSet = null;
// Call readRequest to get the result
resultSet = db.readRequest(sql);
try {
while (resultSet.next()) {
data[0] = resultSet.getString("reply_content");
data[1] = resultSet.getString("reply_by");
// Add data to table model
tableModel1.addRow(data);
}
resultSet.close();
} catch (Exception e) {
System.out.println(e);
}
// add tablemodel to jtable
}
The problem is whenever users post a new reply, the existing posts will be re-added together. I try to do like only the newer reply from the comment box will be added into the jTable instead of keep on re-add the existing posts with newer reply. What am I supposed to use? A for loop? Thanks in advance.
The correct way to delete the content of DefaultTableModel is
model.setRowCount(0);
vs. the evil way mentioned in the comment (won't repeat it here ;-) which violates two rules
never change the underlying datastructure of a model under its feet
never call any of the model's fireXX from code outside the model
If doing the latter seems to help, it's a waring signal: you either violated the former or your model implementation is incorrect

How do you get the DataContext of a LINQ to SQL Entity?

Currently this is what I have:
public partial class LinqToSqlEntity {
public IQueryable<AnotherLinqToSqlEntity> AnotherLinqToSqlEntities {
using(DataContext context = new DataContext) {
return context.AnotherLinqToSqlEntities.Where(item => item.Property == SOME_VALUE);
}
}
}
Is there a way to get the DataContext of this so that I would not need to create a new DataContext?
Sorry, that is not possible. An entity or querable in that case keeps no direct reference of the context.
You can achieve that using the reflection by figuring out if PropertyChanging event was hooked up, but consider this a hack and maybe you can avoid using it with better design.
Our use case of this is on detach_EntityName delegate where we change the default Linq behaviour of only deleting the foreign key of a record (setting it to null), with the actual delete from DB.
public static DataContext GetDataContextFromEntityObject(object entity)
{
// Use a reflection to get the invocaiton list.
var o = (PropertyChangingEventHandler)entity.GetType().GetField("PropertyChanging", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(entity);
var o = GetFieldValue(entity, "PropertyChanging");
if (o == null) return null;
var invocationList = o.GetInvocationList();
if (invocationList != null)
{
// DataContext changes are tracked through StandardChangeTracker
object changeTracker = (from i in invocationList where i.Target.GetType().FullName == "System.Data.Linq.ChangeTracker+StandardChangeTracker" select i.Target).FirstOrDefault();
if (changeTracker != null)
{
object services = GetFieldValue(changeTracker, "services");
return (DataContext)GetFieldValue(services, "context");
}
}
return null;
}
private static object GetFieldValue(object instance, string propertyName)
{
return instance.GetType().GetField(propertyName, BindingFlags.Instance | BindingFlags.NonPublic).GetValue(instance);
}

Find out what fields are being updated

I'm using LINQ To SQL to update a user address.
I'm trying to track what fields were updated.
The GetChangeSet() method just tells me I'm updating an entity, but doesn't tell me what fields.
What else do I need?
var item = context.Dc.Ecs_TblUserAddresses.Single(a => a.ID == updatedAddress.AddressId);
//ChangeSet tracking
item.Address1 = updatedAddress.AddressLine1;
item.Address2 = updatedAddress.AddressLine2;
item.Address3 = updatedAddress.AddressLine3;
item.City = updatedAddress.City;
item.StateID = updatedAddress.StateId;
item.Zip = updatedAddress.Zip;
item.Zip4 = updatedAddress.Zip4;
item.LastChangeUserID = request.UserMakingRequest;
item.LastChangeDateTime = DateTime.UtcNow;
ChangeSet set = context.Dc.GetChangeSet();
foreach (var update in set.Updates)
{
if (update is EberlDataContext.EberlsDC.Entities.Ecs_TblUserAddress)
{
}
}
Use ITable.GetModifiedMembers. It returns an array of ModifiedMemberInfo objects, one for each modified property on the entity. ModifiedMemberInfo contains a CurrentValue and OriginalValue, showing you exactly what has changed. It's a very handy LINQ to SQL feature.
Example:
ModifiedMemberInfo[] modifiedMembers = context.YourTable.GetModifiedMembers(yourEntityObject);
foreach (ModifiedMemberInfo mmi in modifiedMembers)
{
Console.WriteLine(string.Format("{0} --> {1}", mmi.OriginalValue, mmi.CurrentValue));
}
You can detect Updates by observing notifications of changes. Notifications are provided through the PropertyChanging or PropertyChanged events in property setters.
E.g. you can extend your generated Ecs_TblUserAddresses class like this:
public partial class Ecs_TblUserAddresses
{
partial void OnCreated()
{
this.PropertyChanged += new PropertyChangedEventHandler(User_PropertyChanged);
}
protected void User_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
string propertyName = e.PropertyName;
// do what you want
}
}
Alternatively, if you want to track a special property changing, you could use one of those OnPropertyNameChanging partial methods, e.g. (for City in your example):
partial void OnCityChanging(string value)
{
// value parameter holds a new value
}

Model binding issue in LINQ to SQL

I am just now starting to work with LINQ, and am pretty familiar with MVC. I have a strongly typed view that is updating a record. I have successfully done a creation:
This works fine, and creates a record in the database:
public ActionResult Create(TABLEMODEL tableModel)
{
DBDataContext db = new DBDataContext();
if (ModelState.IsValid)
{
db.TABLEMODEL.InsertOnSubmit(tableModel);
db.SubmitChanges();
}
}
But when trying to update:
public ActionResult Manage(TABLEMODEL tableModel)
{
DBDataContext db = new DBDataContext();
if (ModelState.IsValid)
{
db.SubmitChanges();
}
}
This fails, in the sense that it does not update the record in the database. No actual error/exception occurs, and I can step through it just fine.
I am sure I am missing something, but cannot find what. I appreciate any help on this matter.
UPDATE
I did notice that if I get a record using the DataContext:
DBDataContext db = new DBDataContext();
var m = db.TABLEMODELs.Single(m => m.ID == 1);
m.Name = "UpdatedName";
db.SubmitChanges();
This does update, so I assume I am somehow not binding from my model to the LINQ context.
My Solution
I found that you need to retrieve the object and then update that with the form. Simple enough.
[HttpPost]
public ActionResult Manage(int ID, FormCollection form)
{
DBSDataContext db = new DBSDataContext();
var t= db.TABLEMODELs.Single(b => b.ID == ID);
UpdateModel(t);
if (ModelState.IsValid)
{
db.SubmitChanges();
}
return View(t);
}
You should re-query the original tableModel, map the updated row and then update.
Perhaps something like this (example only, not knowing anything about your schema):
var originalTableModel = db.GetById( tableModel.Id);
originalTableModel.FirstName = tableModel.FirstName;
db.SubmitChanges();