Makale Özeti

Tüm yazılımcı arkadaşlara selamlar, Bu makalemde raporlama işleminin öneminden, geçmiş sürümlerde ve dillerde nasıl geliştirildiğinden ve son olarak Microsoft Visual Studio.NET (Vs.NET) projelerinde bu sorunlara, rapor oluşturmak,görüntülemek ve dağıtım sonrası harici rapor dosyalarının kullanımı hakkında bilgi vererek nasıl çözüm getirildiğinden bahsedeceğim.

Makale

Tüm yazılımcı arkadaşlara selamlar,

Bu makalemde raporlama işleminin öneminden, geçmiş sürümlerde ve dillerde nasıl geliştirildiğinden ve son olarak Microsoft Visual Studio.NET (Vs.NET) projelerinde bu sorunlara, rapor oluşturmak,görüntülemek ve dağıtım sonrası harici rapor dosyalarının kullanımı hakkında bilgi vererek nasıl çözüm getirildiğinden bahsedeceğim.

Giriş
Raporlama işlemi yazılımların başarılı olmasında kilit rol oynayan öğelerden birisi olmuştur.Eğer kullanıcı bir yazılımdan herhangi bir şekilde sağlıklı bir rapor alamıyorsa yazılım ne kadar iyi olursa olsun eksi puan alması kaçınılmazdır.Yazılımın sektörünün ilk dilleri olan C,Pascal,Qbasic gibi dillerde raporlama işlemi tamamen programcı'ya yüklenmişti.LPT1 portu o dönemlerde emin olun çok meşhurdu.Ardından DBASE,Paradox,FoxPro gibi veritabanı gömülü diller ile birlikte bu ürünlerin kendi getirdikleri raporlama araçları programcıların yükünü biraz olsun hafifletti.Visual dillerin kullanıma sunulmasıyla ve Deskjet yazıcıların yaygınlaşmasıyla birlikte raporlama araçlarında büyük bir gelişme sağlandı.Yazılım geliştirme araçlarının sunduğu raporlama teknolojilerinin yetersiz kaldığı noktalarda 3rd party ürünler kullanıma sunuldu.Fakat bütçesi dar olan projelerde bu iş tekrar programcılar üzerine yüklendi.Yazılımların kendi print preview bileşenleri geliştirildi,yazıcıların programcılar için olan kitapçıkları baştan aşağı okundu.Örneğin Visual Studio 6.0 da data report ile raporlarınızı başarılı bir şekilde alabiliyor fakat Microsoft Excel'e veya Word'e çıktı alabilmek için biraz çaba sarfetmeniz, datareport'u arkaplanda html olarak kaydedip tekrar arkaplanda webbrowser kontrolünü kullanarak açıp sendmessage apileriyle html sayfasının tümünü kopyalayıp word uygulamasına yapıştırmanız gerekiyordu.Bu basit bir çözüm getiriyor fakat ticari uygulamalarda yetersiz kalıyordu.

Microsoft, Visual Studio.NET raporlama aracı enterprise raporlama gerekmedikçe bu sorunlara çözüm getirmiştir.

Basit bir raporlama projesi geliştirelim
İsterseniz artık bu çok sözettiğimiz .NET raporlama aracını daha iyi anlamak için bir proje geliştirelim.Basit bir örnekle başlayalım, bir tablomuz olsun ve günlük kur bilgilerini tutsun.Veritabanı olarak Vs.NET ile birlikte gelen "Microsoft SQL Server 2005 Express Edition" ı kullanacağız.Gerekli olan sütunlar kur tarihi,kodu ve miktarı.



Veritabanını ve tabloyu yaratmak için gerekli t-Sql kodu;

USE [master]
GO
CREATE DATABASE [TestDatabase] ON PRIMARY 
( NAME = N'TestDatabase', FILENAME = N'C:\LEO_SQLDB\DATA\TestDatabase.mdf' , SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
LOG ON 
( NAME = N'TestDatabase_log', FILENAME = N'C:\LEO_SQLDB\LOG\TestDatabase_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
COLLATE Turkish_CI_AS
GO
EXEC dbo.sp_dbcmptlevel @dbname=N'TestDatabase', @new_cmptlevel=90
GO
IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
begin
EXEC [TestDatabase].[dbo].[sp_fulltext_database] @action = 'enable'
end
GO
ALTER DATABASE [TestDatabase] SET ANSI_NULL_DEFAULT OFF 
GO
ALTER DATABASE [TestDatabase] SET ANSI_NULLS OFF 
GO
ALTER DATABASE [TestDatabase] SET ANSI_PADDING OFF 
GO
ALTER DATABASE [TestDatabase] SET ANSI_WARNINGS OFF 
GO
ALTER DATABASE [TestDatabase] SET ARITHABORT OFF 
GO
ALTER DATABASE [TestDatabase] SET AUTO_CLOSE OFF 
GO
ALTER DATABASE [TestDatabase] SET AUTO_CREATE_STATISTICS ON 
GO
ALTER DATABASE [TestDatabase] SET AUTO_SHRINK OFF 
GO
ALTER DATABASE [TestDatabase] SET AUTO_UPDATE_STATISTICS ON 
GO
ALTER DATABASE [TestDatabase] SET CURSOR_CLOSE_ON_COMMIT OFF 
GO
ALTER DATABASE [TestDatabase] SET CURSOR_DEFAULT GLOBAL 
GO
ALTER DATABASE [TestDatabase] SET CONCAT_NULL_YIELDS_NULL OFF 
GO
ALTER DATABASE [TestDatabase] SET NUMERIC_ROUNDABORT OFF 
GO
ALTER DATABASE [TestDatabase] SET QUOTED_IDENTIFIER OFF 
GO
ALTER DATABASE [TestDatabase] SET RECURSIVE_TRIGGERS OFF 
GO
ALTER DATABASE [TestDatabase] SET ENABLE_BROKER 
GO
ALTER DATABASE [TestDatabase] SET AUTO_UPDATE_STATISTICS_ASYNC OFF 
GO
ALTER DATABASE [TestDatabase] SET DATE_CORRELATION_OPTIMIZATION OFF 
GO
ALTER DATABASE [TestDatabase] SET TRUSTWORTHY OFF 
GO
ALTER DATABASE [TestDatabase] SET ALLOW_SNAPSHOT_ISOLATION OFF 
GO
ALTER DATABASE [TestDatabase] SET PARAMETERIZATION SIMPLE 
GO
ALTER DATABASE [TestDatabase] SET READ_WRITE 
GO
ALTER DATABASE [TestDatabase] SET RECOVERY SIMPLE 
GO
ALTER DATABASE [TestDatabase] SET MULTI_USER 
GO
ALTER DATABASE [TestDatabase] SET PAGE_VERIFY CHECKSUM 
GO
ALTER DATABASE [TestDatabase] SET DB_CHAINING OFF 
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[EXC_TBL](
[EXC_DATE] [datetime] NOT NULL,
[EXC_CUR] [varchar](3) COLLATE Turkish_CI_AS NOT NULL,
[EXC_RATE] [money] NOT NULL
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF

Vs.NET de yeni bir "Windows Uygulaması" açalım.Yarattığımız veritabanını proje içinde kullanabilmemiz için bir "datasource" nesnesi yaratmamız gerekmektedir.Projemizdeki "Datasource" nesnelerini "Data Sources" çerçevesinden görebilir yeni bağlantılar ekleyebiliriz.

Projemize yeni bir "Datasource" eklemek için "Add New Data Source" bağlantısını tıklayın ve yaratmış olduğumuz veritabanına bağlanmak için gerekli bilgileri girin.

 

TestDatabaseDataSet ismiyle datasource nesnemizi yarattıktan sonra artık raporumuzu hazırlama işlemine geçebiliriz.

Bir "Windows Uygulaması"’nda rapor oluşturmamız için iki nesneye ihtiyacımız olacaktır.Bunlardan ilki Report dosyası.Bu dosya raporumuzun dizayn ve veri bilgilerini tutan dosyadır.İkinci nesnemiz ise oluşturduğumuz rapor dosyasını görüntüleyeceğimiz ReportViewer nesnesidir.

Projemize bir rapor dosyası eklemek için "Solution Explorer" çerçevesinde sağ click yapın.Beliren menüden "Add" / "New Item" seçeneğini tıklayın.

Açılan ekrandan "Report" seçeneğini seçip "Ok" tuşuna basın.

Böylece projemize yeni bir Rapor dosyası eklemiş olduk.

Rapor dosyası aşağıdaki şekildeki gibi görünecektir.

Araçkutusu çerçevesinde Rapor dosyamızda kullanabileceğimiz nesneler belirecektir.Verilerimizi görüntülemek için bir Table ve rapor başlığımız için bir TextBox nesnesini raporun üzerine sürükleyin.

DataView çerçevesini kullanarak daha önce eklediğimiz TestDatabaseDataSet altındaki EXC_TBL alanlarını Table nesnesinin kolonlarına sürükleyip bırakın.Sürüklediğimiz alanlar başlıklarıyla birlikte Table nesnesine eklenmiş olacaktır.İsterseniz başlıkları değiştirebilirsiniz.TextBox nesnesine ise raporumuzun başlığını yazabiliriz.

Evet artık raporumuzun tasarımını bitirdik.Artık tek ihtiyacımız rapor nesnesini görüntülemek için ReportViewer nesnesini kullanmak.Bunun için Form1.cs [design] sekmesine geri dönelim ve araçkutusundan formun üzerine bir "ReportViewer" nesnesi sürükleyip bırakalım.

"ReportViewer tasks" ekranından "Choose a report" seçeneğinde daha önce projemize eklediğimiz Report1.rdlc dosyasını seçelim.

Ve projemiz çalışmaya hazır.Projemizi çalıştırıp sonuçları görelim.

 


ReportViewer nesnesi araççubuğunu kullanarak mevcut görüntülenen raporu Microsoft Excel (xls) veya Adobe Acrobat (pdf) dosya formatında saklayabilirsiniz.

Buraya kadar basit olarak nasıl rapor oluşturup görüntüleyeceğimizi işledik.Peki bu tasarımdaki eksiklikler ne olabilir? 

1.Diyelimki tasarımdan ötürü veritabanı bağlantılarımızı kendi sınıflarımızı yazarak kod ile gerçekleştiriyoruz.Bu tür bir tasarımda "Data Sources" çerçevemizde herhangi bir datasource nesnesi görünmeyecektir.Dolayısı ile rapor dosyamızı tasarım aşamasında dizayn ve test edemeyeceğiz.

Hemen bir testini yapalım.Yeni bir "Windows Uygulaması" açalım ve bir rapor nesnesi ekleyelim.Ardından rapor nesnesine bir Table nesnesi ekleyelim."Data Sources" çerçevesinde herhangi bir nesne bulunmadığından dolayı bu şekilde projeyi derlemeye çalışalım.

Alacağımız hata "The table 'table1' is in the report body but the report has no data set. Data regions are not allowed in reports without datasets." olacaktır.Yani Vs.NET idesinden bir datasource eklemezsek, tasarım zamanında bir rapor dosyası oluşturamıyoruz.Evet bu sorunu çalışma zamanında rapor dosyasına nesneler ekleyerek çözebiliriz fakat bu tamamen bir eziyet olacaktır.

2.Rapor dosyası "embedded resource" projeye gömülü bir kaynak olarak kaydedileceğinden dolayı her rapor değişikliğinde bir derleme ve dağıtım işlemi gerektirecektir.Bu da basit bir rapor dizaynından dolayı proje sürümünün değişmesi demek olur ki bu da önemli bir eksikliktir.

Çözüm: Harici rapor dosyalarının kullanımı
Harici rapor dosyalarının bir önceki örneğimizde geliştirdiğimiz rapor dosyalarından hiçbir farkı yoktur.Raporları harici kılan bizim onları kullanış şeklimiz olacaktır.Normal işleyişteki tıkandığımız nokta datasource nesnemiz olmadığından dolayı tasarım zamanında rapor nesnesini yaratamıyor oluşumuzdu.İlk önce bu problemi aşmamız gerekiyor.Bildiğiniz üzere .NET ile gelen en büyük yeniliklerden birisi tamamen XML desteğine sahip olmasıydı.Yani biz kod yazarak veritabanına ulaşabilir, verilerimizi Dataset nesnemize doldurabilir ve tablomuzu xml olarak kaydedebiliriz.XML yeni nesil bir veri depolama sistemi olduğundan dolayı bu dosyaları projemize ekleyebilir ve datasource nesneleri elde edebiliriz.Bunun için EXC_TBL tablosunun XML schema dosyasını oluşturmamız gerekmektedir.

<?xml version="1.0" standalone="yes"?>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="EXC_TBL" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="EXC_TBL">
<xs:complexType>
<xs:sequence>
<xs:element name="EXC_DATE" type="xs:dateTime" />
<xs:element name="EXC_CUR">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="3" />
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="EXC_RATE" type="xs:decimal" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>

XML diline yakın arkadaşlar kolayca tabloların schema dosyalarını oluşturacaktır.Tabi çok büyük çaplı tablolarda bu zaman kaybı olacaktır.XML Schema dosyası veri veritabanından okunduktan ve bir datatable'a aktarıldan sonra kod ile yaratılabilir.Aşağıdaki örnek MyDataset nesnesindeki EXC_TBL tablosunun XML Schema bilgisini "C:\EXC_TBL.xsd" dosyasına yazacaktır.

MyDataset.Tables["EXC_TBL"].WriteXmlSchema(@"c:\EXC_TBL.xsd")

Bu açıklamalardan sonra çözüm projemizi geliştirmeye başlayabiliriz.

Yeni bir "Windows Uygulaması" açalım.Projeye varsayılan olarak gelen Form1 winform nesnemize ek olarak Form2 winform nesnesini ekleyelim.Form1'e 1 textbox, 2 button, 1 openfileDialog ve son olarak bir errorProvider nesnesi ekleyelim.

 Button2 nesnesi bizim daha önce oluşturduğumuz bir rapor dosyasını seçmemizi sağlayacaktır.Click olayına aşağıdaki kodu yazalım.

private void button2_Click(object sender, EventArgs e)
{
textBox1.Text= (openFileDialog1.ShowDialog() == DialogResult.OK)?openFileDialog1.FileName:textBox1.Text;
}

Böylece button2 ye tıkladığımızda openfiledialog devreye girecek ve bizden bir rapor (rdlc) dosyası seçmemizi isteyecektır.
Projemizde bulunan diğer buton'a bastığımızda ise veritabanına bağlanılacak, veri okunacak ve Form2 yüklenerek textbox1 de seçilen rapor dosyası görüntülenecektir.

Bu işlemden önce projemize bir rapor dosyası ekleyelim.Ardından daha önce XML schema dosyasını oluşturduğumuz EXC_TBL.xsd dosyasını projemize ekleyelim.

Dikkat ederseniz eklediğimiz xsd dosyası "Data Sources" çerçevesinde bir datasource kaydı olarak gözükecektir.

Evet artık bir datasource'umuz olduğuna göre raporumuzu dilediğimiz gibi tasarlayabiliriz.İlk projemizde yaptığımız gibi Araçkutusundan bir Table nesnesini raporumuzun üzerine sürükleyip bırakalım.Ardından NewDataSet datasource kaydımızdan gerekli olan EXC_DATE,EXC_CUR ve EXC_RATE alanlarını Tablo üzerine yerleştirelim.İlk projemizle uyumlu olması için bir TextBox nesnesi ile Raporumuzun başlığını oluşturalım.

Geriye Button1'in click olayında veritabanına bağlanıp veriyi sorgulamak ve Form2'yi yükleyip çalışma zamanında bir ReportViewer nesnesi oluşturup Raporumuzu görüntülemek için gerekli kodu yazmak kaldı.

private void button1_Click(object sender, EventArgs e)
{
#region Tanimlamalar
SqlConnection MyConnection = new SqlConnection();
SqlCommand MyCommand = new SqlCommand();
DataSet MyDataset = new DataSet();
#endregion Tanimlamalar

#region Dogrulama
if (textBox1.Text.Length == 0)
errorProvider1.SetError(textBox1, "Bu alan doldurulmalıdır.");
if (!File.Exists(textBox1.Text))
errorProvider1.SetError(textBox1,"Dosya bulunamadı.");
#endregion Dogrulama

#region Baglanti
MyConnection.ConnectionString = @"Data Source=IT001\SQLEXPRESS;Initial Catalog=TestDatabase;Integrated Security=True;Network Library=dbmssocn";
try
{
MyConnection.Open();
}
catch(SqlException ExcpSQL)
{
MessageBox.Show("'" + ExcpSQL.Message + "' hatası oluştu");
return;
}
#endregion Baglanti

#region VeriOkuma
MyCommand.CommandType = CommandType.Text;
MyCommand.Connection = MyConnection;
MyCommand.CommandText = "Select * from EXC_TBL";

try
{
SqlDataReader MyDatareader = MyCommand.ExecuteReader();
MyDataset.Load(MyDatareader, LoadOption.OverwriteChanges, new string[] { "EXC_TBL" });
//MyDataset.Tables["EXC_TBL"].WriteXmlSchema(@"c:\EXC_TBL.xsd");
}
catch (Exception Excp)
{
MessageBox.Show("'" + Excp.Message + "' hatası oluştu");
return;
}
#endregion VeriOkuma

#region RaporIslemleri
try
{
Form RaporFormu = new Form2();
RaporFormu.Text = "";

Microsoft.Reporting.WinForms.ReportViewer rpvViewer = new Microsoft.Reporting.WinForms.ReportViewer();
RaporFormu.Controls.Add(rpvViewer);
rpvViewer.Dock = DockStyle.Fill;

RaporFormu.Show();

rpvViewer.LocalReport.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource("NewDataSet_EXC_TBL", MyDataset.Tables["EXC_TBL"]));
rpvViewer.LocalReport.ReportPath = textBox1.Text;


rpvViewer.RefreshReport();
}
catch (Exception Excp)
{
MessageBox.Show("'" + Excp.Message + "' hatası oluştu");
return;
}
#endregion RaporIslemleri

}

Tanımlamalar,Doğrulama,Bağlantı ve Veri Okuma region ları hakkında yorum yapmaya gerek yok sanırım.Önemli bölüm RaporIslemleri region'ında bulunan kod.

rpvViewer.LocalReport.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource("NewDataSet_EXC_TBL", MyDataset.Tables["EXC_TBL"]));

Bu satıda raporumuzun kullandığı datasource'u bildiriyoruz.Mevcut datasource'umuz bir xsd yani Schema dosyası olduğundan, veri içermediğinden dolayı bu şemayı ve veritabanından okuduğumuz bilgileri birleştirerek bir ReportDataSource nesnesi oluşturuyoruz.Oluşturduğumuz bu nesneyi ise LocalReport.DataSources öğesine atıyoruz.

rpvViewer.LocalReport.ReportPath = textBox1.Text;

Bu satırda ise rapor dosyasının yolunu ReportPath öğesine atıyoruz.

Projemizi çalıştırıp test edelim ve publish ile dağıtımını yaratalım.Ardından kurulum dosyasını çalıştırıp kurulumu gerçekleştirelim.Projemiz program files altında görünecektir,projemizin dağıtımı tamamlanmıştır.

Şimdi senaryo üretelim ve mevcut EXC_TBL tablomuza ait yeni bir rapor gereksinimi olduğunu varsayalım.İlk projemizde böyle bir problemle karşılaştığımızda yeni bir sürüm derlememiz gerekecekti.Çözüm projemizde ise böyle bir sorunumuz yok.

Vs.NET IDE'sine geri dönelim ve hemen projeye yeni bir rapor dosyası ekleyelim.Bu rapor dosyasına Araçkutusundan bir Chart nesnesi ekleyelim ve aşağıdaki gibi ayarlarını yapalım.



Bu rapor dosyasını saklayalım.Ardından daha önce kurulumunu gerçekleştirdiğimiz projemizi program files mesüsünden çalıştıralım.Bu sefer son olarak tasarladığımız Report2.rdlc dosyasını seçelim ve Button1 butonuna tıklayalım.



Böylece artık veri yapısı değişmedikçe EXC_TBL tablosuna istediğimiz kadar rapor geliştirebilir bu rapor dosyalarını yeni sürüm derlemeye gerek kalmadan kullanıma sunabiliriz. 


Evet bir makalenin daha sonuna geldik.Umarım yararlı olmuştur.
Hepinize mutlu günler dilerim. 

Levent YILDIZ
theone@leventyildiz.net
msmoracle@hotmail.com 

Kaynaklar
http://msdn.microsoft.com/