(ASP.NET) ASP.NET (2008 год)

Формирование Excel-отчетов


На этой страничке я опишу один маленький, но важный вопросик из моего приложения для тестиорвания работников РЖД. Некая тестовая версия этого моего приложения осталась у меня на хостинге - rzd.vb-net.ru. В этом приложении вообще много интересных моментов, например момент расчета времени для тестирования - синхронизатор серверного отсчета времени и клиентского устаревания странички через SetTimeOut, и много чего еще интересного почти на каждой из форм. Но я опишу на своем хомячке лишь одну форму. Потому что я увидел десяток вопросов в рунете - как вызвать Excel или Word в Web-приложении или Web-службе - и в основном был ответ - это невозможно, это нарушает безопасность.

Это конечно же - бред. DCOM-конфигуратор именно предназначен для описания конкретики этого вызова - и мы посмотрим на этой страничке и как это настраивать и как писать собственно код странички, формирующей вот такой Excel-отчет из базы.

Изначально, вот эту страничку, которую вы видите на рисунке (и можете войти в нее по ссылке выше - зайти под начальником станции и в результаты испытаний->просмотреть->РБУ-10 - только дату отчета конечную выберете больше чем начальную) - так вот, эту страничку я сначала написал на отчете Кристала. Вообще у меня много раз получалось сформировать на кристале отличные отчеты - но в этом случае все уперлось и ни в какую у меня не получилось добится требуемого форматирования. Тогда я оставил в покое кристалл и докрутил на этой же страничке формирование отчета в Excel - и это сразу устроило заказчика.

Мы рассмотрим сначала как сформировать такой отчет, а потом посмотрим, как настроить DCOM и настроить все неоходимое для работы этой страничке на хостинге.





Итак, форма, скрин которой вы видите - просто формирует параметры вызова формы RBU10.aspx, которую мы и будем рассматривать подробнее, включая настройку ее работы на хостинге.


Сама по себе форма RBU10.aqspx выглядит практически пустой - содержит пару запросов в базу и обьявление сборки кристала.

00001: <%@ Page Language="VB" AutoEventWireup="false" CodeFile="RBU10.aspx.vb" Inherits="RBU10" %>
00002: 
00003: <%@ Register assembly="CrystalDecisions.Web, Version=10.5.3700.0, Culture=neutral, PublicKeyToken=692fbea5521e1304" namespace="CrystalDecisions.Web" tagprefix="CR" %>
00004: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
00005: 
00006: <html xmlns="http://www.w3.org/1999/xhtml">
00007: <head runat="server">
00008:     <title>Печать РБУ-10</title>
00009: </head>
00010: <body>
00011:     <form id="form1" runat="server">
00012:     <div>
00013:         <CR:CrystalReportViewer ID="CrystalReportViewer1" runat="server"  EnableDatabaseLogonPrompt="False" EnableParameterPrompt="False"  />
00014:     </div>
00015:     <asp:Label ID="Err1" runat="server" ForeColor="Red" ></asp:Label>
00016:     
00017:     <asp:SqlDataSource ID="GetCheckPlan0" runat="server" 
00018:         ConnectionString="<%$ ConnectionStrings:SQLServer_ConnectionStrings %>" 
00019:         SelectCommand="CheckPlan0" SelectCommandType="StoredProcedure">
00020:         <SelectParameters>
00021:             <asp:QueryStringParameter Name="Group"  QueryStringField="Group" />
00022:             <asp:QueryStringParameter Name="StartDate" Type="DateTime" QueryStringField="StartDate"  />
00023:             <asp:QueryStringParameter Name="StopDate" Type="DateTime"  QueryStringField="StopDate"  />
00024:         </SelectParameters>
00025:     </asp:SqlDataSource>
00026:     
00027:     <asp:SqlDataSource ID="CheckPlan0_OneUser" runat="server" 
00028:         ConnectionString="<%$ ConnectionStrings:SQLServer_ConnectionStrings %>" 
00029:         SelectCommand="CheckPlan0_OneUser" SelectCommandType="StoredProcedure">
00030:         <SelectParameters>
00031:             <asp:QueryStringParameter Name="Group"  QueryStringField="Group" />
00032:             <asp:QueryStringParameter Name="ToUser" Type="String"  QueryStringField="J"  />
00033:         </SelectParameters>
00034:     </asp:SqlDataSource>
00035:     
00036:     <asp:SqlDataSource ID="GetSchemaName" runat="server" 
00037:         ConnectionString="<%$ ConnectionStrings:SQLServer_ConnectionStrings %>" 
00038:         SelectCommand="select top 1 * from [Schema] where id=@id">
00039:         <SelectParameters>
00040:              <asp:Parameter Name="id" Type="String" />
00041:         </SelectParameters>
00042:     </asp:SqlDataSource>
00043:     
00044:     
00045:     </form>
00046:     </body>
00047:     
00048: </html>

А вот код у этой формы более интересный. Приведу его целиком.

00001: 'параметры формы
00002: 'Debug=1  - перечисление колонок отчета для построителя отчета
00003: 'GroupID=fa541063-fa8b-4d90-b4e8-d27c32808b4c - ID группы - передается в отборы
00004: 'GroupName=СеверныйПост - имя группы - печатается на формах
00005: 'StartDate=30.12.2008
00006: 'StopDate=21.02.2012
00007: 'может быть задано J=57717A5E-69CB-4DD0-9831-3682F01CA696 - гуид юзера - тогда нужен отчет только по одному юзеру
00008: 'Mode="Crystal" or Mode="Excel"
00009: Partial Class RBU10
00010:     Inherits System.Web.UI.Page
00011: 
00012: 
00013:     Protected Sub RBU10_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
00014:         If Request.QueryString("GroupID") = "" Then
00015:             Err1.Text = "Не задан обязательный параметр GroupID"
00016:             Exit Sub
00017:         End If
00018:         If Request.QueryString("GroupName") = "" Then
00019:             Err1.Text = "Не задан обязательный параметр GroupName"
00020:             Exit Sub
00021:         End If
00022:         If Request.QueryString("StartDate") = "" Then
00023:             Err1.Text = "Не задан обязательный параметр StartDate"
00024:             Exit Sub
00025:         End If
00026:         If Request.QueryString("StopDate") = "" Then
00027:             Err1.Text = "Не задан обязательный параметр StopDate"
00028:             Exit Sub
00029:         End If
00030:         'If Request.QueryString("Mode") = "" Then
00031:         '    Err1.Text = "Не задан обязательный параметр Mode"
00032:         '    Exit Sub
00033:         'End If
00034:         'все данные из плана
00035:         Dim DV As Data.DataView
00036:         If Request.QueryString("J") Is Nothing Then
00037:             'отбор по группе
00038:             If Roles.IsUserInRole(User.Identity.Name, "SuperAdmin") Or Roles.IsUserInRole(User.Identity.Name, "Admin") Or Roles.IsUserInRole(User.Identity.Name, "Tutor") Or Roles.IsUserInRole(User.Identity.Name, "SubTutor") Then
00039:                 GetCheckPlan0.SelectParameters("Group").DefaultValue = Request.QueryString("GroupID")
00040:                 GetCheckPlan0.SelectParameters("StartDate").DefaultValue = Request.QueryString("StartDate")
00041:                 GetCheckPlan0.SelectParameters("StopDate").DefaultValue = Request.QueryString("StopDate")
00042:                 Try
00043:                     DV = GetCheckPlan0.Select(New DataSourceSelectArguments)
00044:                 Catch X As SystemException
00045:                     Err1.Text = "Ошибка отбора. Проверьте COLLATE базы." & vbCrLf & X.ToString
00046:                     Exit Sub
00047:                 End Try
00048:             Else
00049:                 CrystalReportViewer1.Visible = False
00050:                 Err1.Text = "У вас недостаточно прав для получения этого отчета."
00051:                 Exit Sub
00052:             End If
00053:         Else
00054:             'отбор по одному юзеру
00055:             CheckPlan0_OneUser.SelectParameters("Group").DefaultValue = Request.QueryString("GroupID")
00056:             CheckPlan0_OneUser.SelectParameters("ToUser").DefaultValue = Request.QueryString("i")
00057:             Try
00058:                 DV = CheckPlan0_OneUser.Select(New DataSourceSelectArguments)
00059:             Catch X As SystemException
00060:                 Err1.Text = "Ошибка отбора. Проверьте COLLATE базы." & vbCrLf & X.ToString
00061:                 Exit Sub
00062:             End Try
00063:         End If
00064:         If DV Is Nothing Then
00065:             Err1.Text = "Нет результатов по данному отбору GroupName=" & Request.QueryString("GroupName") & ", StartDate=" & Request.QueryString("StartDate") & ", StopDate=" & Request.QueryString("StopDate")
00066:             If Request.QueryString("J") IsNot Nothing Then
00067:                 Err1.Text &= "<br>" & "По пользователю " & Request.QueryString("J")
00068:             End If
00069:             Exit Sub
00070:         End If
00071:         If DV.Count = 0 Then
00072:             Err1.Text = "Нет результатов по данному отбору GroupName=" & Request.QueryString("GroupName") & ", StartDate=" & Request.QueryString("StartDate") & ", StopDate=" & Request.QueryString("StopDate")
00073:             If Request.QueryString("J") IsNot Nothing Then
00074:                 Err1.Text &= "<br>" & "По пользователю " & Request.QueryString("J")
00075:             End If
00076:             Exit Sub
00077:         End If
00078:         Dim DT As Data.DataTable = DV.ToTable
00079:         'плюс наименование билета
00080:         DT.Columns.Add("Schema", GetType(System.String))
00081:         'плюс все из профиля
00082:         DT.Columns.Add("RailWay", GetType(System.String))
00083:         DT.Columns.Add("Department", GetType(System.String))
00084:         DT.Columns.Add("Station", GetType(System.String))
00085:         DT.Columns.Add("Location", GetType(System.String))
00086:         DT.Columns.Add("FIO", GetType(System.String))
00087:         DT.Columns.Add("Telefon", GetType(System.String))
00088:         DT.Columns.Add("ForNew", GetType(System.String))
00089:         DT.Columns.Add("ForOld", GetType(System.String))
00090:         DT.Columns.Add("Num", GetType(System.Int32))
00091:         DT.Columns.Add("TestResult_String", GetType(System.String))
00092:         DT.Columns.Add("SummaryResume_String", GetType(System.String))
00093:         DT.Columns.Add("StartDate_String", GetType(System.String))
00094:         DT.Columns.Add("PlainDate_String", GetType(System.String))
00095:         For Each OneRow As Data.DataRow In DT.Rows
00096:             '
00097:             Dim MyTypedProfile As ProfileCommon = ProfileBase.Create(Membership.GetUser(New Guid(OneRow("ToUser").ToString)).UserName)
00098:             If MyTypedProfile IsNot Nothing Then
00099:                 OneRow("Location") = MyTypedProfile.Location
00100:                 OneRow("FIO") = MyTypedProfile.FIO
00101:                 OneRow("Telefon") = MyTypedProfile.Telefon
00102:                 OneRow("ForNew") = MyTypedProfile.ForNew
00103:                 OneRow("ForOld") = MyTypedProfile.ForOld
00104:                 'текстовые названия станций
00105:                 Dim RZD As New GetRZD_Names(MyTypedProfile)
00106:                 OneRow("RailWay") = RZD.RailWay
00107:                 OneRow("Department") = RZD.Department
00108:                 OneRow("Station") = RZD.Station
00109:                 'прочее предформатирование для отчета
00110:                 If Not IsDBNull(OneRow("TestResult")) Then
00111:                     If OneRow("TestResult") Then
00112:                         OneRow("TestResult_String") = "сдал"
00113:                     Else
00114:                         OneRow("TestResult_String") = "не сдал"
00115:                     End If
00116:                 End If
00117:                 If IsDBNull(OneRow("StartDate")) Then
00118:                     OneRow("StartDate_String") = "не сдавал"
00119:                 End If
00120:                 If Not IsDBNull(OneRow("SummaryResume")) Then
00121:                     If OneRow("SummaryResume") = 1 Then
00122:                         OneRow("SummaryResume_String") = "сдал"
00123:                     ElseIf OneRow("SummaryResume") = 2 Then
00124:                         OneRow("SummaryResume_String") = "пересдача"
00125:                     ElseIf OneRow("SummaryResume") = 3 Then
00126:                         OneRow("SummaryResume_String") = "не сдал"
00127:                     End If
00128:                 End If
00129:                 If Not IsDBNull(OneRow("PlainDate")) Then
00130:                     OneRow("PlainDate_String") = String.Format("{0:dd.MM.yy}", OneRow("PlainDate"))
00131:                 End If
00132:             End If
00133:             '
00134:             GetSchemaName.SelectParameters("ID").DefaultValue = OneRow("ToSchema").ToString
00135:             Dim DT1 As Data.DataView = GetSchemaName.Select(New DataSourceSelectArguments)
00136:             If DT1 IsNot Nothing Then
00137:                 If DT1.Count > 0 Then
00138:                     OneRow("Schema") = DT1(0)("Title")
00139:                 End If
00140:             End If
00141:         Next
00142:         '
00143:         'это отладчик построителя форм
00144:         If Request.QueryString("Debug") <> "" Then
00145:             Response.Write("<Html><body><table border=1>")
00146:             For Each OneColumn As Data.DataColumn In DT.Columns
00147:                 Response.Write("<tr><td>" & OneColumn.ColumnName & "</td><td>" & OneColumn.DataType.Name & "</td></tr>")
00148:             Next
00149:             Response.Write("</table></body></html>")
00150:             Exit Sub
00151:         End If
00152:         '
00153:         'пересортируем отчет по фамилиям
00154:         Dim DV2 As Data.DataView = DT.DefaultView
00155:         DV2.Sort = "UserName,PlainDate"
00156:         '
00157:         'перенумеруем
00158:         If DV2.Count > 0 Then
00159:             Dim J As Integer = 1
00160:             DV2(0)("Num") = J
00161:             For i As Integer = 0 To DV2.Count - 2
00162:                 If DV2(i + 1)("UserName") <> DV2(i)("UserName") Then
00163:                     J += 1
00164:                     DV2(i + 1)("Num") = J
00165:                 End If
00166:             Next
00167:         End If
00168:         '
00169:         'почистить лишние пробелы в строках
00170:         For i As Integer = 0 To DV.Count - 1
00171:             If Not IsDBNull(DV2(i)("NoPlanComment")) Then DV2(i)("NoPlanComment") = DV(i)("NoPlanComment").ToString.Trim '    String
00172:             If Not IsDBNull(DV2(i)("Comment")) Then DV2(i)("Comment") = DV2(i)("Comment").ToString.Trim '    String
00173:             If Not IsDBNull(DV2(i)("Schema")) Then DV2(i)("Schema") = DV2(i)("Schema").ToString.Trim '    String
00174:             If Not IsDBNull(DV2(i)("RailWay")) Then DV2(i)("RailWay") = DV2(i)("RailWay").ToString.Trim '    String
00175:             If Not IsDBNull(DV2(i)("Department")) Then DV2(i)("Department") = DV2(i)("Department").ToString.Trim '    String
00176:             If Not IsDBNull(DV2(i)("Station")) Then DV2(i)("Station") = DV2(i)("Station").ToString.Trim '    String
00177:             If Not IsDBNull(DV2(i)("Location")) Then DV2(i)("Location") = DV2(i)("Location").ToString.Trim '    String
00178:             If Not IsDBNull(DV2(i)("FIO")) Then DV2(i)("FIO") = DV2(i)("FIO").ToString.Trim '    String
00179:             If Not IsDBNull(DV2(i)("Telefon")) Then DV2(i)("Telefon") = DV2(i)("Telefon").ToString.Trim '    String
00180:         Next
00181: 
00182:         '
00183:         If Request.QueryString("Mode") = "Excel" Then
00184:             GoExcel(DV2)
00185:         Else
00186:             GoCrystal(DV2)
00187:         End If
00188:     End Sub
00189: 
00190:     Sub GoCrystal(ByVal DV As Data.DataView)
00191:         '-------------------------
00192:         'теперь берем бланк отчета
00193:         Dim MyReport As New CrystalDecisions.CrystalReports.Engine.ReportDocument
00194:         MyReport.Load(Server.MapPath("RBU10.rpt"))
00195:         'заталкиваем в бланк сформированные на этой форме данные
00196:         MyReport.SetDataSource(DV)
00197:         CrystalReportViewer1.ReportSourceID = ""
00198:         'и заталкиваем в кристал-вьювеер отчет уже с данными
00199:         CrystalReportViewer1.ReportSource = MyReport
00200:         'передаем во вьюверер параметры этой формы
00201:         If CrystalReportViewer1.ParameterFieldInfo("GroupName") IsNot Nothing Then SetReportParm("GroupName", Request.QueryString("GroupName"))
00202:         If CrystalReportViewer1.ParameterFieldInfo("StartDate") IsNot Nothing Then SetReportParm("StartDate", Request.QueryString("StartDate"))
00203:         If CrystalReportViewer1.ParameterFieldInfo("StopDate") IsNot Nothing Then SetReportParm("StopDate", Request.QueryString("StopDate"))
00204:         'и рисуем вьювеером отчет на форме
00205:         CrystalReportViewer1.DataBind()
00206:     End Sub
00207: 
00208:     Sub GoExcel(ByVal DV As Data.DataView)
00209:         'Сначала открываем бланк
00210:         Dim RBU10_TempName As String = "RBU10_" & Guid.NewGuid.ToString & ".xls"
00211:         Dim RBU10_FullTempName As String = System.IO.Path.Combine(Server.MapPath("Excel_Temp"), RBU10_TempName)
00212:         '
00213:         If Not My.Computer.FileSystem.FileExists(System.IO.Path.Combine(Server.MapPath("~"), "RBU10_blank.xls")) Then
00214:             Err1.Text = "Отсуствует файл шаблона RBU10_blank.xls"
00215:             Exit Sub
00216:         Else
00217:             My.Computer.FileSystem.CopyFile(System.IO.Path.Combine(Server.MapPath("~"), "RBU10_blank.xls"), RBU10_FullTempName)
00218:         End If
00219:         Dim XL As New Microsoft.Office.Interop.Excel.Application
00220:         Dim XL_WB As Microsoft.Office.Interop.Excel.Workbook = XL.Workbooks.Open(RBU10_FullTempName, 0, False)
00221:         Dim XL_Page As Microsoft.Office.Interop.Excel.Worksheet = XL_WB.ActiveSheet
00222:         '
00223:         'собственно обработка
00224:         If Request.QueryString("ExcelDebug") IsNot Nothing Then
00225:             'сырой вывод базы
00226:             XL_Page.Cells(9, 1) = "id(Guid)"
00227:             XL_Page.Cells(9, 2) = "ToFirst(Guid)"
00228:             XL_Page.Cells(9, 3) = "ToUser(Guid)"
00229:             XL_Page.Cells(9, 4) = "ToSchema(Guid)"
00230:             XL_Page.Cells(9, 5) = "IsPlan(Boolean)"
00231:             XL_Page.Cells(9, 6) = "IsNoPlan(Boolean)"
00232:             XL_Page.Cells(9, 7) = "NoPlanComment(String)"
00233:             XL_Page.Cells(9, 8) = "PlainDate(DateTime)"
00234:             XL_Page.Cells(9, 9) = "StartDate(DateTime)"
00235:             XL_Page.Cells(9, 10) = "TestGuid(Guid)"
00236:             XL_Page.Cells(9, 11) = "TestResult(Boolean)"
00237:             XL_Page.Cells(9, 12) = "Comment(String)"
00238:             XL_Page.Cells(9, 13) = "SummaryResume(Int32)"
00239:             XL_Page.Cells(9, 14) = "ForBeginnersPO(DateTime)"
00240:             XL_Page.Cells(9, 15) = "ForBeginnersCT(DateTime)"
00241:             XL_Page.Cells(9, 16) = "ForBeginnersPC(DateTime)"
00242:             XL_Page.Cells(9, 17) = "UserName(String)"
00243:             XL_Page.Cells(9, 18) = "Schema(String)"
00244:             XL_Page.Cells(9, 19) = "RailWay(String)"
00245:             XL_Page.Cells(9, 20) = "Department(String)"
00246:             XL_Page.Cells(9, 21) = "Station(String)"
00247:             XL_Page.Cells(9, 22) = "Location(String)"
00248:             XL_Page.Cells(9, 23) = "FIO(String)"
00249:             XL_Page.Cells(9, 24) = "Telefon(String)"
00250:             XL_Page.Cells(9, 25) = "ForNew(String)"
00251:             XL_Page.Cells(9, 26) = "ForOld(String)"
00252:             XL_Page.Cells(9, 27) = "Num(Int32)"
00253:             XL_Page.Cells(9, 28) = "TestResult_String(String)"
00254:             XL_Page.Cells(9, 29) = "SummaryResume_String(String)"
00255:             XL_Page.Cells(9, 30) = "StartDate_String(String)"
00256:             XL_Page.Cells(9, 31) = "PlainDate_String(String)"
00257:             XL_Page.Cells(9, 32) = "GroupID"
00258:             XL_Page.Cells(9, 33) = "GroupName"
00259:             XL_Page.Cells(9, 34) = "StartDate"
00260:             XL_Page.Cells(9, 35) = "StopDate"
00261:             For i As Integer = 0 To DV.Count - 1
00262:                 If Not IsDBNull(DV(i)("id")) Then XL_Page.Cells(10 + i, 1) = DV(i)("id").ToString() '    Guid
00263:                 If Not IsDBNull(DV(i)("ToFirst")) Then XL_Page.Cells(10 + i, 2) = DV(i)("ToFirst").ToString '    Guid
00264:                 If Not IsDBNull(DV(i)("ToUser")) Then XL_Page.Cells(10 + i, 3) = DV(i)("ToUser").ToString '    Guid
00265:                 If Not IsDBNull(DV(i)("ToSchema")) Then XL_Page.Cells(10 + i, 4) = DV(i)("ToSchema").ToString '    Guid
00266:                 If Not IsDBNull(DV(i)("IsPlan")) Then XL_Page.Cells(10 + i, 5) = DV(i)("IsPlan").ToString '    Boolean
00267:                 If Not IsDBNull(DV(i)("IsNoPlan")) Then XL_Page.Cells(10 + i, 6) = DV(i)("IsNoPlan").ToString '    Boolean
00268:                 If Not IsDBNull(DV(i)("NoPlanComment")) Then XL_Page.Cells(10 + i, 7) = DV(i)("NoPlanComment").ToString '    String
00269:                 If Not IsDBNull(DV(i)("PlainDate")) Then XL_Page.Cells(10 + i, 8) = DV(i)("PlainDate").ToString '    DateTime
00270:                 If Not IsDBNull(DV(i)("StartDate")) Then XL_Page.Cells(10 + i, 9) = DV(i)("StartDate").ToString '    DateTime
00271:                 If Not IsDBNull(DV(i)("TestGuid")) Then XL_Page.Cells(10 + i, 10) = DV(i)("TestGuid").ToString '    Guid
00272:                 If Not IsDBNull(DV(i)("TestResult")) Then XL_Page.Cells(10 + i, 11) = DV(i)("TestResult").ToString '    Boolean
00273:                 If Not IsDBNull(DV(i)("Comment")) Then XL_Page.Cells(10 + i, 12) = DV(i)("Comment").ToString '    String
00274:                 If Not IsDBNull(DV(i)("SummaryResume")) Then XL_Page.Cells(10 + i, 13) = DV(i)("SummaryResume").ToString '    Int32
00275:                 If Not IsDBNull(DV(i)("ForBeginnersPO")) Then XL_Page.Cells(10 + i, 14) = DV(i)("ForBeginnersPO").ToString '    DateTime
00276:                 If Not IsDBNull(DV(i)("ForBeginnersCT")) Then XL_Page.Cells(10 + i, 15) = DV(i)("ForBeginnersCT").ToString '    DateTime
00277:                 If Not IsDBNull(DV(i)("ForBeginnersPC")) Then XL_Page.Cells(10 + i, 16) = DV(i)("ForBeginnersPC").ToString '    DateTime
00278:                 If Not IsDBNull(DV(i)("UserName")) Then XL_Page.Cells(10 + i, 17) = DV(i)("UserName").ToString '    String
00279:                 If Not IsDBNull(DV(i)("Schema")) Then XL_Page.Cells(10 + i, 18) = DV(i)("Schema").ToString '    String
00280:                 If Not IsDBNull(DV(i)("RailWay")) Then XL_Page.Cells(10 + i, 19) = DV(i)("RailWay").ToString '    String
00281:                 If Not IsDBNull(DV(i)("Department")) Then XL_Page.Cells(10 + i, 20) = DV(i)("Department").ToString '    String
00282:                 If Not IsDBNull(DV(i)("Station")) Then XL_Page.Cells(10 + i, 21) = DV(i)("Station").ToString '    String
00283:                 If Not IsDBNull(DV(i)("Location")) Then XL_Page.Cells(10 + i, 22) = DV(i)("Location").ToString '    String
00284:                 If Not IsDBNull(DV(i)("FIO")) Then XL_Page.Cells(10 + i, 23) = DV(i)("FIO").ToString '    String
00285:                 If Not IsDBNull(DV(i)("Telefon")) Then XL_Page.Cells(10 + i, 24) = DV(i)("Telefon").ToString '    String
00286:                 If Not IsDBNull(DV(i)("ForNew")) Then XL_Page.Cells(10 + i, 25) = DV(i)("ForNew").ToString '    String
00287:                 If Not IsDBNull(DV(i)("ForOld")) Then XL_Page.Cells(10 + i, 26) = DV(i)("ForOld").ToString '    String
00288:                 If Not IsDBNull(DV(i)("Num")) Then XL_Page.Cells(10 + i, 27) = DV(i)("Num").ToString '    Int32
00289:                 If Not IsDBNull(DV(i)("TestResult_String")) Then XL_Page.Cells(10 + i, 28) = DV(i)("TestResult_String").ToString '    String
00290:                 If Not IsDBNull(DV(i)("SummaryResume_String")) Then XL_Page.Cells(10 + i, 29) = DV(i)("SummaryResume_String").ToString '    String
00291:                 If Not IsDBNull(DV(i)("StartDate_String")) Then XL_Page.Cells(10 + i, 30) = DV(i)("StartDate_String").ToString '    String
00292:                 If Not IsDBNull(DV(i)("PlainDate_String")) Then XL_Page.Cells(10 + i, 31) = DV(i)("PlainDate_String").ToString '    String
00293:                 XL_Page.Cells(10 + i, 32) = Request.QueryString("GroupID")
00294:                 XL_Page.Cells(10 + i, 33) = Request.QueryString("GroupName")
00295:                 XL_Page.Cells(10 + i, 34) = Request.QueryString("StartDate")
00296:                 XL_Page.Cells(10 + i, 35) = Request.QueryString("StopDate")
00297:             Next
00298:         Else
00299:             'чистовой отчет
00300:             XL_Page.Cells(8, 14) = "Служебные пометки АС ДО" : XL_Page.Cells(8, 14).Font.Bold = True : XL_Page.Cells(8, 14).Font.Color = RGB(255, 0, 0)
00301:             XL_Page.Cells(9, 14) = "RailWay" : XL_Page.Cells(9, 14).Font.Bold = True
00302:             XL_Page.Cells(9, 15) = "Department" : XL_Page.Cells(9, 15).Font.Bold = True
00303:             XL_Page.Cells(9, 16) = "Station" : XL_Page.Cells(9, 16).Font.Bold = True
00304:             XL_Page.Cells(9, 17) = "ToUser" : XL_Page.Cells(9, 17).Font.Bold = True
00305:             XL_Page.Cells(9, 18) = "UserName" : XL_Page.Cells(9, 18).Font.Bold = True
00306:             XL_Page.Cells(9, 19) = "ToSchema" : XL_Page.Cells(9, 19).Font.Bold = True
00307:             XL_Page.Cells(9, 20) = "Schema" : XL_Page.Cells(9, 20).Font.Bold = True
00308:             XL_Page.Cells(9, 21) = "id" : XL_Page.Cells(9, 21).Font.Bold = True
00309:             XL_Page.Cells(9, 22) = "TestGuid" : XL_Page.Cells(9, 22).Font.Bold = True
00310:             XL_Page.Cells(9, 23) = "ToFirst" : XL_Page.Cells(9, 23).Font.Bold = True
00311:             XL_Page.Cells(9, 24) = "GroupID" : XL_Page.Cells(9, 24).Font.Bold = True
00312:             XL_Page.Cells(9, 25) = "GroupName" : XL_Page.Cells(9, 25).Font.Bold = True
00313:             XL_Page.Cells(9, 26) = "StartDate" : XL_Page.Cells(9, 26).Font.Bold = True
00314:             XL_Page.Cells(9, 27) = "StopDate" : XL_Page.Cells(9, 27).Font.Bold = True
00315:             For i As Integer = 0 To DV.Count - 1
00316:                 If Not IsDBNull(DV(i)("Num")) Then
00317:                     'новый юзер
00318:                     XL_Page.Cells(10 + i, 1) = DV(i)("Num").ToString '    Int32
00319:                     XL_Page.Cells(10 + i, 11) = "один раз в два года"
00320:                     For j As Integer = 1 To 100
00321:                         XL_Page.Cells(10 + i, j).Font.Bold = True
00322:                     Next
00323:                 End If
00324: 
00325:                 If Not IsDBNull(DV(i)("Location")) Then XL_Page.Cells(10 + i, 2) = DV(i)("Location").ToString
00326:                 If Not IsDBNull(DV(i)("FIO")) Then XL_Page.Cells(10 + i, 3) = DV(i)("FIO").ToString '    String
00327:                 If Not IsDBNull(DV(i)("ForBeginnersPO")) Then XL_Page.Cells(10 + i, 4) = DV(i)("ForBeginnersPO").ToString '    DateTime
00328:                 If Not IsDBNull(DV(i)("ForBeginnersCT")) Then XL_Page.Cells(10 + i, 5) = DV(i)("ForBeginnersCT").ToString '    DateTime
00329:                 If Not IsDBNull(DV(i)("ForBeginnersPC")) Then XL_Page.Cells(10 + i, 6) = DV(i)("ForBeginnersPC").ToString '    DateTime
00330:                 If DV(i)("IsPlan") Then
00331:                     If Not IsDBNull(DV(i)("PlainDate_String")) Then XL_Page.Cells(10 + i, 7) = DV(i)("PlainDate_String").ToString '    String
00332:                 Else
00333:                     If Not IsDBNull(DV(i)("PlainDate_String")) Then XL_Page.Cells(10 + i, 12) = DV(i)("PlainDate_String").ToString '    String
00334:                 End If
00335:                 If Not IsDBNull(DV(i)("StartDate_String")) Then XL_Page.Cells(10 + i, 8) = DV(i)("StartDate_String").ToString '    String
00336:                 If Not IsDBNull(DV(i)("TestResult_String")) Then XL_Page.Cells(10 + i, 9) = DV(i)("TestResult_String").ToString '    String
00337:                 If Not IsDBNull(DV(i)("SummaryResume_String")) Then XL_Page.Cells(10 + i, 10) = DV(i)("SummaryResume_String").ToString '    String
00338:                 If Not IsDBNull(DV(i)("Comment")) Then XL_Page.Cells(10 + i, 13) = DV(i)("Comment").ToString '    String
00339:                 If Not IsDBNull(DV(i)("RailWay")) Then XL_Page.Cells(10 + i, 14) = DV(i)("RailWay").ToString '    String
00340:                 If Not IsDBNull(DV(i)("Department")) Then XL_Page.Cells(10 + i, 15) = DV(i)("Department").ToString '    String
00341:                 If Not IsDBNull(DV(i)("Station")) Then XL_Page.Cells(10 + i, 16) = DV(i)("Station").ToString '    String
00342:                 If Not IsDBNull(DV(i)("ToUser")) Then XL_Page.Cells(10 + i, 17) = DV(i)("ToUser").ToString '    Guid
00343:                 If Not IsDBNull(DV(i)("UserName")) Then XL_Page.Cells(10 + i, 18) = DV(i)("UserName").ToString '    String
00344:                 If Not IsDBNull(DV(i)("ToSchema")) Then XL_Page.Cells(10 + i, 19) = DV(i)("ToSchema").ToString '    Guid
00345:                 If Not IsDBNull(DV(i)("Schema")) Then XL_Page.Cells(10 + i, 20) = DV(i)("Schema").ToString '    String
00346:                 If Not IsDBNull(DV(i)("id")) Then XL_Page.Cells(10 + i, 21) = DV(i)("id").ToString() '    Guid
00347:                 If Not IsDBNull(DV(i)("TestGuid")) Then XL_Page.Cells(10 + i, 22) = DV(i)("TestGuid").ToString '    Guid
00348:                 If Not IsDBNull(DV(i)("ToFirst")) Then XL_Page.Cells(10 + i, 23) = DV(i)("ToFirst").ToString '    Guid
00349:                 XL_Page.Cells(10 + i, 24) = Request.QueryString("GroupID")
00350:                 XL_Page.Cells(10 + i, 25) = Request.QueryString("GroupName")
00351:                 XL_Page.Cells(10 + i, 26) = Request.QueryString("StartDate")
00352:                 XL_Page.Cells(10 + i, 27) = Request.QueryString("StopDate")
00353:             Next
00354:         End If
00355:         'сохраняем на диск
00356:         XL_WB.Close(True)
00357:         XL = Nothing
00358:         '
00359:         'и записываем все в поток браузера
00360:         Response.ContentType = "attachment"
00361:         Response.Charset = "utf-8"
00362:         Response.AppendHeader("content-disposition", "attachment; filename=" & RBU10_TempName)
00363:         Context.Response.BinaryWrite(My.Computer.FileSystem.ReadAllBytes(RBU10_FullTempName))
00364: 
00365:     End Sub
00366: 
00367:     Private Sub SetReportParm(ByVal ReportParmName As String, ByVal Value As Object)
00368:         Dim ExecParms As CrystalDecisions.Shared.ParameterValues = New CrystalDecisions.Shared.ParameterValues()
00369:         Dim OnePrm As CrystalDecisions.Shared.ParameterDiscreteValue = New CrystalDecisions.Shared.ParameterDiscreteValue()
00370:         OnePrm.Value = Value
00371:         ExecParms.Add(OnePrm)
00372:         CrystalReportViewer1.ParameterFieldInfo(ReportParmName).CurrentValues = ExecParms
00373:     End Sub
00374: 
00375: 
00376: End Class

Как видите, тут есть и код работы с кристалом (строки 190-206, 367-373), который у меня многократно разжеван на сайте (и тут мы его по новой жевать не будем), код запроса в базу и корректировки сырых данных из базы (строки 1-188) и код работы с Excel (строки 208-365).

В строке 35 обьявляется датавью, который заполняется запросом из базы. Затем он расширяется в строке 78-95 еще на несколько колонок (для данных, которые хранятся в профиле пользователя). Затем в строках 95-180 все это подчищается, пересортировывается, коды заменяются на слова в итоговом отчете.

Работа с Экселом (строки 208-365) заключается во взятии файла Excel c шапкой отчета RBU10_blank.xls, затем он копируется во временную директорию Excel_Temp c уникальным именем и открывается там в строках 219-221. Затем он заполняется в строках 225-297 для отладочного отчета, который просто перечислит выведетт все, что есть в сформированном нами датасете (с указанием типа данных), либо в строках 300-354 формируется чистовой отчет - по формату, требуемому РЖД. Затем в строке 356 Эксел закрывается с сохранением сформированных в нем данных и в строках 360-363 этот временный файл с уникальным именем из директории Excel_Temp - пишется в поток браузера.

Я сделал это перечисление полей в датасете (строки 226-296) - для своего удобства - для проверки какие в итоге поля у меня получились в сформированном датасете и их тип данных.



Теперь рассмотрим, пожалуй, даже более интересную тему (чем собственно программирование этой формы) - вынос этого сайта на хостинг.


Прежде всего, этот сайт непросто вынести на хостинг, даже из-за наличия в нем кристалловских сборок. Дело в том, что часть кристалловских сборок подключается из ГАКА. Чтобы вычислить все сборки, задействованные при компиляции - необходимо открыть студией PRECOMPILED-сайт. И увидеть в окне OUTPUT перечень сборок, необходимых для компиляции. Ибо они могут быть версии 2.7, 2.8 или вообще какого-то иного релиза, если кристал был установлен отдельно или обновлен.

Но даже копирование на хостинг всех этих сборок - не гарантирует корректную работу кристала. Несмотря на то, что сборка CrystalDecisions.ReportAppServer.CommLayer отсутсвует в списке линкуемых с сайтом сборок - обычно получается чудесное исключение:

System.TypeInitializationException: Инициализатор типа "CrystalDecisions.CrystalReports.Engine.ReportDocument" выдал исключение. ---> System.IO.FileNotFoundException: 
Невозможно загрузить файл или сборку "CrystalDecisions.ReportAppServer.CommLayer, Version=10.5.3700.0, Culture=neutral, PublicKeyToken=692fbea5521e1304" 
или один из зависимых от них компонентов. Не удается найти указанный файл.
Имя файла: "CrystalDecisions.ReportAppServer.CommLayer, Version=10.5.3700.0, Culture=neutral, PublicKeyToken=692fbea5521e1304"
в CrystalDecisions.CrystalReports.Engine.ReportDocument..cctor()

Предупреждение: регистрация привязки сборок выключена.
Чтобы включить регистрацию ошибок привязки сборок, установите значение параметра реестра [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) в 1.
Примечание. Регистрация ошибок привязки сборок может привести к некоторому снижению производительности.
Чтобы отключить эту функцию, удалите параметр реестра [HKLM\Software\Microsoft\Fusion!EnableLog].

Несмотря на полезность журналирования привязки сборок - это очень похоже на обычный баг (тк эту сборку отдельно я вообще не нашел). А вообще Crystal - ацкая по обьему продуктов компания - например только для Crystal Reports (а еще есть Crystal Reports Server и Xcelsius) - основные версии их продуктов - .NET,10,2,2008,3,4,4.5,5,6,6.5,7,8,8.5,9,XI,XI Release 1,XI Release 2. Так вот в версии Crystal Reports .NET - существует 87 продуктов! Всевозможные патчи, дополнения, сервис паки и прочее. Мне стало лень разбиратся в этом баге - поскольку требуемого форматирования я так и не смог добится от кристала (из-за чего и переделал отчет на Excel) и в этом проекте корректную установку ран-тайм среды для кристала я оставил админам РЖД .



Теперь давайте посмотрим, что надо сделать, чтобы на хостинге заработал Excel. Как я говорил - в рунете самое распространенное мнение, что вообще невозможно вызывать Excel и Word из Web-сервисов на сайтах. По крайней мере я не нашел никаких рекомнедаций и настроил все сам (хотя после этой моей заметки они наверняка уже появятся).

Так, вот - сначала довольно тривиальная вещь. Как это можно убедится, у меня на хостинге этот сайт запущен под учеткой Network Services (хотя ничто мне не мешает сделать это иначе). Значит, первое действие - это права на запись на эту учетку в директорию Excel_Temp, куда сайт копирует временные файлы строкой 217 и куда Excel сохраняет свою страничку после записи в нужные клетки странички нужных данных их базы (строка 356). Иначе - обычный отказ в доступе.

Следующая тривиальная вешь - Excel в принципе должен присутствовать на хостинге, причем именно той версии, под которой компилировался сайт. Если он отсутсвует на хостинге - то в этом сообщении будут просто нули вместо GUID'а Excel'а.

Но даже если все это сделано (те доступ ко всем диреториям есть и сам Excel есть) - cайт все равно упадет.

Сбой при получении фабрики класса COM для компонента с CLSID {00024500-0000-0000-C000-000000000046} в результате следующей ошибки: 80070005. 
Описание: Необработанное исключение при выполнении текущего веб-запроса. Изучите трассировку стека для получения дополнительных сведений о данной ошибке и о вызвавшем ее фрагменте кода. 

Сведения об исключении: System.UnauthorizedAccessException: Сбой при получении фабрики класса COM для компонента с CLSID {00024500-0000-0000-C000-000000000046} в результате следующей ошибки: 80070005. 
ASP.NET не имеет права обращаться к запрошенному ресурсу. Рекомендуется предоставить идентификатору запроса ASP.NET права доступа к этому ресурсу. ASP.NET имеет базовый идентификатор процесса 
(обычно {MACHINE}\ASPNET для IIS 5 или Network Service на IIS 6), который используется, если приложение не олицетворяется. Если приложение олицетворяется через задание <identity impersonate="true"/>, 
идентификатором будет служить идентификатор анонимного пользователя (обычно IUSR_MACHINENAME) или идентификатор пользователя запроса с проверенной подлинностью. 

Для предоставления ASP.NET прав на запись в файл, щелкните на файле правой кнопкой мыши в окне "Проводник", выберите "Свойства", затем вкладку "Безопасность". 
Выберите "Добавить" для добавления соответствующего пользователя или группы. Выделите учетную запись ASP.NET и установите флажки для требуемых прав доступа.

И на этом этапе широкая публика обычно скисает. А между тем требуется всего два простых шага для разруливания этой ситуации.


В первую очередь, на потребуется самое распространенное действие - разрешение на ключ реестра. Это широко известный шаг в среде профессиональных программистов-асперов. У меня много раз это действие упоминается на сайте. Например, на этой страничке разрешение на ключ HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\EventLog необходимо, чтобы сайт мог писать в журнал. Поэтому, наш первый шаг - разрешение на ключ реестра COM-класса Excel'а HKEY_CLASSES_ROOT\CLSID\{00024500-0000-0000-C000-000000000046}. Естественно, на ту учетку, под которой работает домен приложения - в моем случае это Network Services.

И второй шаг, необходимость которого тоже туговато доходит до широкой публики. Набираем от командной строки DCOMCNFG. Находим в оснастке служба компонентов Excel. Конфигурировать его там на пяти вкладках и десятках дочерних вкладок - можно ой как по-разному. Но самый примитивный способ для начинающих - отключить вообще проверку подлинности.



И если вы все это сделали правильно, вы будете вознаграждены - подобные отчеты ваш сайт будет формировать так же легко, как и мой:



Удачи!



Комментарии к этой страничке ( )
ссылка на эту страничку: http://www.vb-net.ru/asp2/39/index.htm
<На главную>  <В раздел ASP>  <В раздел NET>  <В раздел SQL>  <В раздел Разное>  <Написать автору>  < Поблагодарить>
Московская хельсинская группа   Радио Свобода  Новая газета   The New Times (Новое время)