2016年6月3日 星期五

升級CrystalReport遇到的狀況

昨天升級完.NET從2.0到3.5,想說應該都OK了,結果測一下報表發現有錯誤訊息,錯誤點在於要將ReportSource指到CrystalReportSource.ReportDocument.Load時發生問題,訊息如下"為具有 CLSID {5FF57840-5172-4482-9CA3-541C7878AE0F} 的元件擷取 COM Class Factory 失敗: 800736b1",看到這訊息直覺就是元件沒安裝好,但又覺得那悶,我裝了之前從官網上下載的CrystalReport安裝檔For VS2008的,檔名是CrystalReports2007_x86.msi(印象中之前裝好像可以用的說),後來又用手動把元件重新註冊好像還是一樣,最後想到在安裝VS2008時,安裝程式都會把CrystalReport的安裝檔放在電腦的某個地方,位置在C:\Program Files (x86)\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\CrystalReports10_5,裡面有x86跟x64的版本,想說試看看用這個版本裝到Server上,結果就正常啟動了,呼~~,終於解決了

2016年6月2日 星期四

升級.NET Framwork遇到的怪事

今天為了一些原因把網站從.NET 2.0 升級到.NET 3.5,轉完後再VS上一切都正常,架在公司的測試機Windows 2003 Server上也都正常執行,想說沒甚麼問題要把它佈署到正式環境上發生了一件事,請看圖

在Web.config出現了此訊息"已經定義過區段或群組名稱 'scriptResourceHandler'。您只能在定義它的組態層級中更新它",一開始還以為是正式機上的.NET沒升上3.5或.NET AJAX沒安裝,但經測試後都不是,原因在於正式機的網站上有架一個站台上面是跑.NET 2.0的,我在此網站上建立一個虛擬目錄,Web.config是設定.NET 3.5,原來是這樣導致IIS一直說我重複載入。如果站台沒有指定.NET Framework則各自虛擬目錄底下的版本可以各自獨立。

2016年5月20日 星期五

JSNlog 結合NLog與Elmah

原本只有在使用Elmah及NLog,前幾天看到有一個東西叫JSNLog,而且可以整合在一起,終於有個方便的東西可以記錄JS到底發生了甚麼事,直接來看怎麼使用吧。
  1. 使用Nuget安裝以下幾個套件
    • Elmah相關:Elmah、Elmah Code Library、Elmah MVC、Elmah On Xml Log
    • NLog相關:NLog、NLog.config、NLog.schema、NLog.elmah
    • JSNLog相關: JSNLog、JSNLog.NLog
  2. 安裝完就來做設定,基本上就兩個地方,第一個是NLog.config要設定寫到Elmah的格式,第二個就是在頁面上要放jsnlog.js檔案
    • NLog.config的部分
    • <targets> 
          <target xsi:type="Elmah"
                  name="elmahWithLogLevelAsType"
                  layout="
                  Log等級:${level:uppercase=true}
                  Source:${callsite:className=true}
                  錯誤訊息:${message}
                  StackTrace:${exception:format=stacktrace}
                  Exception類別:${exception:format=type}
                  Exception訊息:${exception:format=message}
                  EXCEPTION OCCURRED:         ${exception:format=type,message,method:maxInnerExceptionLevel=5:innerFormat=shortType,message,method}"
                  LogLevelAsType="true" />
      <targets />
      <rules>
          <logger name="*" minlevel="Trace" writeTo="elmahWithLogLevelAsType" />
      <rules />
      
    • 因為我是用MVC專案來做,所以只要把引用的段落放在_Layout裡
    • //這段加在_Layout.cshtml的最所有JS檔的最後面
      @Html.Raw(JSNLog.JavascriptLogging.Configure())
      
  3. 再來就可以測試是否成功,在瀏覽器上用console輸入JL().info("test info"),看是否可正常送回後台,再來就是查看Elmah是否成功紀錄此訊息。
     
參考來源:JSNLog with ASP MVC Web

2016年5月13日 星期五

iTextSharp將HTML轉成PDF

之前就有用過iTextSharp將PDF合併,這次又需要用到它了。事情是這樣的,原本在專案裡使用的是AspPDF這個套件,把HTML的檔案轉成PDF做輸出給使用者下載,但最近客戶要換機,從Windows2003到Windows2012,本想說把套件裝一裝就可以了,但事情沒那麼簡單,這要追朔到當時接此專案的歷史,就不多說了,結論就是不能用。於是想到之前用過iTextSharp,印象中是免費又好用的套件,話不說就來看看如何實作~~
  1. 此專案當時是用.NET 3.5 vs2008 開發的,當然就沒有好用的NuGet,只好自己下載來安裝,這邊要下載兩個套件iTextSharpXmlWorker,基本上可以直接載最新版的,載下來後把參考加到專案中,
    如果可以使用NuGet就直接搜尋安裝即可。
  2. 這邊放上完整的程式碼,主要是抓取HTML的路徑把文字檔轉成byte產出PDF實體檔案
  3. public void print()
    {
        WebClient wc = New WebClient;
        wc.Encoding = Encoding.UTF8;
        string htmlText = wc.DownloadString("path");
        byte[] pdfFile = ConvertHtmlTextToPDF(htmlText);
        //將PDF產生出實體檔案
        File.WriteAllBytes("fileName", pdfFile);
    }
    
    public byte[] ConvertHtmlTextToPDF(string htmlText)
    {
        if (string.IsNullOrEmpty(htmlText)) {
            return null;
        }
        //
        htmlText = "" + htmlText + "
    "; MemoryStream outputStream = new MemoryStream(); byte[] data = Encoding.UTF8.GetBytes(htmlText); MemoryStream msInput = new MemoryStream(data); //A4橫印 //iTextSharp.text.Document doc = new iTextSharp.text.Document( new iTextSharp.text.Rectangle(288f, 144f), 10, 10, 10, 10); //doc.SetPageSize(iTextSharp.text.PageSize.A4.Rotate()); //A4直印 iTextSharp.text.Document doc = new iTextSharp.text.Document(); iTextSharp.text.pdf.PdfWriter writer = iTextSharp.text.pdf.PdfWriter.GetInstance(doc, outputStream); iTextSharp.text.pdf.PdfDestination pdfDest = new iTextSharp.text.pdf.PdfDestination( iTextSharp.text.pdf.PdfDestination.XYZ, 0, doc.PageSize.Height, 1f); doc.Open(); iTextSharp.tool.xml.XMLWorkerHelper.GetInstance().ParseXHtml( writer, doc, msInput, null, Encoding.UTF8, new UnicodeFontFactory()); iTextSharp.text.pdf.PdfAction action = iTextSharp.text.pdf.PdfAction.GotoLocalPage(1, pdfDest, writer); writer.SetOpenAction(action); doc.Close(); msInput.Close(); outputStream.Close(); return outputStream.ToArray(); }
    支援Unicode的顯示
    using System;
    using System.IO;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Diagnostics;
    public class UnicodeFontFactory : iTextSharp.text.FontFactoryImp
    {
        //arial unicode MS是完整的unicode字型。
        private static string arialFontPath = 
            Path.Combine(
                Path.Combine(
                    Directory.GetParent(
                        Environment.GetFolderPath(Environment.SpecialFolder.System)).FullName, 
                        "Fonts"), "arialuni.ttf");
        //標楷體
        private static string chinesePath = 
            Path.Combine(
                Path.Combine(
                    Directory.GetParent(
                        Environment.GetFolderPath(Environment.SpecialFolder.System)).FullName, 
                        "Fonts"), "KAIU.TTF");
    
        public override iTextSharp.text.Font GetFont(string fontname, string encoding, 
            bool embedded, float size, int style, iTextSharp.text.BaseColor color, bool cached)
        {
            iTextSharp.text.pdf.BaseFont baseFont = iTextSharp.text.pdf.BaseFont.CreateFont(
                chinesePath, iTextSharp.text.pdf.BaseFont.IDENTITY_H, 
                iTextSharp.text.pdf.BaseFont.EMBEDDED);
            //
            return new iTextSharp.text.Font(baseFont, size, style, color);
        }
    }
    
    
  4. 至於在HTML檔案上有幾個要注意的是頁面寬度要設定成100%,如果設定死寬度很有可能會破版,另外這邊樣式的部分主要是支援CSS的,其他早期家在tag上的屬性都不再支援。

參考資料:https://dotblogs.com.tw/shadow/archive/2014/02/09/143891.aspx

2016年4月25日 星期一

EF(Entity Framework)在執行存檔時發生的錯誤

今天在重構某一個功能時發生一個怪事,花了一些時間才找到實際發生的原因,在這邊紀錄一下免得下次發生又要再查一次。

事情是這樣的,我的功能是有一個付款的Table,另外有幾個不同付款來源的Table,這些Table裡都有另外註記一些付款資訊,Table之間有建立關聯,EF也有將關聯拉進來,本來在測試付完款的資料,因一些原因必須回復當時做完付款註記到各來源的Table資料表,這時在ObjectContext.SaveChange()發生錯誤,

錯誤訊息:對資料庫的變更認可成功,但是在更新物件內容時發生錯誤。ObjectContext 可能處於不一致的狀態。內部例外狀況訊息: 發生參考完整性限制式違規: 定義參考條件約束的屬性值在關聯性中的主體物件和相依物件之間不一致。

跳出的錯誤訊息有點看不懂他想表達的意思是甚麼,那這訊息老實說也不是第一次看到了,只是之前遇到的時候趕時間就直接用SQLCommand改寫那個段落, 今天想說一直這樣也不是好辦法,決定來追一下這訊息到底是發生甚麼事了。
 
查了半天終於發現問題點,我簡單敘述一下,我的付款Table裡有一個ID,其它付款來源的Table也有一個付款Table的ID,兩個是有作關連的,今天我把付款來源的Table的付款ID更新為null(因為取消付款),那付款的Table並沒有把這筆作刪除,原因就是這個付款ID,被EF偵測到有關連性但是他在寫回資料庫時判斷關聯資料是有異常的,但實際上直接下SQL是可以被允許的,我個人認為是EF為了確保資料正確性而不允許這樣的動作。

後來我把付款Table的那筆資料在同一個ObjectContent裡一起執行就可以順利更新了。

以後只要有遇到此錯誤訊息,第一件事就是先去查看執行的程序裡有運用到那些Table並且關聯為何,過程是否有不正常寫入資料。

2016年4月13日 星期三

Open XML SDK 利用範本產生WORD

因Office已經標準化成XML格式,要產出標準的Office 2007以上的格式就不可使用舊有的Office Library來產生,這次的專案需求是要利用一個範本檔(docx)作套表產出,是要複製範本的格式,依據資料筆數看要貼幾頁,找到此類別庫Open XML SDK來產出。
目前最新的版本是Open XML SDK 2.5是.NET4.0以上,因專案只到.NET3.5,故使用Open XML SDK 2.0版
Open XML SDK 安裝方式
  1. 手動下載方式
    • 下載位址2.5
    • 下載位址2.0
    • 預設安裝路徑為C:\Program Files (x86)\Open XML SDK\ 
    •  下載完有兩個檔案,一個是主要的DLL檔,另一個是他的工具,工具可以將Office檔案載入,可轉換C#的Code出來,幫你把產生此檔案的程式碼產出來,根本就是程式碼產生器,太方便了吧~~
    •  手動將此DLL載入(C:\Program Files (x86)\Open XML SDK\2.0\lib\DocumentFormat.OpenXml.dll)
  2. 使用NuGet
    • 在套件管理器輸入install-package documentformat.openxml,會抓取最新版,如要裝舊版的在後面加上-Version 1.0.0。
另外要再加入WindowsBase的.NET參考。

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;

/// 
/// 複製範本並取出內容的部分
/// 
/// 
/// 
/// 
/// 
private void CopyWordFile(string fromWordFile, string toWordFile, out string templateText)
{
 if (File.Exists(toWordFile)) {
  File.Delete(toWordFile);
 }
 //
 File.Copy(fromWordFile, toWordFile);
 //
 using (WordprocessingDocument fromDoc = WordprocessingDocument.Open(toWordFile, true)) {
  templateText = fromDoc.MainDocumentPart.Document.Body.InnerXml;
  fromDoc.MainDocumentPart.Document.RemoveAllChildren();
 }
}

/// 
/// 設定WORD檔需要的參數
/// 
/// 
/// 
/// 
private void SetWordDictionary(string templateText, out string docText)
{
 Dictionary keyWordDict = new Dictionary();
 keyWordDict.Add("SEQTXT", 1);
 keyWordDict.Add("YEARXX", "105");
 keyWordDict.Add("DEPTNM", "部門");
 keyWordDict.Add("PLANNM", "計畫");
 keyWordDict.Add("DATEXX", "日期");
 keyWordDict.Add("BGTWTT", "金額");
 //
 ReplaceTemplateString(keyWordDict, templateText, docText);
}

/// 
/// 將WORD檔所設定的參數取代掉
/// 
/// 
/// 
/// 
/// 
private void ReplaceTemplateString(Dictionary keyWordDict, string templateText, out string docText)
{
 foreach (KeyValuePair item in keyWordDict) {
  Regex regex = new Regex(item.Key);
  templateText = regex.Replace(templateText, item.Value);
 }
 //
 docText += templateText;
}

/// 
/// 將XML的文字寫入到WORD檔
/// 
/// 
/// 
/// 
private void SetWordFile(string toFileName, string docText)
{
 using (WordprocessingDocument toDoc = WordprocessingDocument.Open(toFileName, true)) {
  MainDocumentPart mainPart = toDoc.MainDocumentPart;
  Body insertBody = mainPart.Document.AppendChild(new Body());
  insertBody.InnerXml = docText;
 }
}
在實作過程也遇到一些問題
  1. 當WORD檔裡有插入文字方塊時,複製出來的XML再貼回去產出來MSWord會說有錯誤,但強制開還是可以開。
  2. WORD範本檔在複製的時候,格式會有點錯亂,有的正常有的又不正常,後來我的WORD範本用Table來排版就比較正常,不知道是MSWord的問題還是Xml的格式問題。
參考文件:Creating Documents by Using the Open XML

2016年4月3日 星期日

Mac 打開檔案的應用程式,刪除重複的開啟程式

  1. 開啟Terminal.app,先輸入以下指令
  2. $ /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -kill -r -domain local -domain system -domain user
    
  3. 再輸入以下指令
  4. $ killall Finder
    
  5. 再重新開啟Finder點選打開的應用程式,重複的列表就會消失了
參考資料:http://hlchang.com/?p=817