網頁

2013年9月2日 星期一

ASP.NET MVC 與 Elmah.MVC 2.1.1 以及使用 Windows Azure SQL Database 的Schema 修改

將近一年的時間沒有寫關於 Elmah 或 Elmah.MVC 的文章,因為這工具的安裝與使用並不是太過於複雜,所以之前的文章內容都已經含蓋了大部分的功能,而最近透過 NuGet 為專案加入 Elmah.MVC 時發現到版本推進到了 2.1.1,觀察安裝後的內容後有發現到了一些改變,畢竟自從去年 11 月所更新的 2.0.2 之後,一直到今年的七月才又再一次的更新到 2.1.0,這一篇就來看看有做了什麼樣的改變。

 


先來看看之前 Elmah.MVC 2.0.2 原本在 Web.Config 的 appSettings 內容,

image

各個設定值的使用說明:

disableHandler」是否關閉 Elmah.MVC handler 的使用,設定為 true (關閉)的話就是使用原本的 elmah 設定。

disableHandlerErrorFilter」是否關閉系統預設的 HandlerErrorAttribure 功能,設定為 true 就是關閉預設功能。

requiresAuthentication」是否需要登入驗證,進入 Elmah Dashboard 是否需要驗證,預設為 false。

allowedRoles」如果前項登入驗證有開啟的話,那麼此項設定就可以加入允許設定的使用者群組(角色)名稱。

route」設定進入 Elmah Dashboard 的路由名稱,預設為「elmah」。

以上為 Elmah.MVC 2.0.2 所提供的五個系統使用設定,有涵蓋到基本的使用功能,但是 route 的設定雖然可以讓我們自己去定義路由名稱,但是原本的 elmah 路徑還是一樣可以使用,但只是讓 Elmah Dashboard 的樣式不正常而已,如下:

image

在「ELMAH.MVC 2.0.1 - 可以自訂瀏覽路徑」這篇文章裡有曾經提到過,如果有在「elmah.mvc.route」去修改路由名稱的話,就必須要在 RouteConfig.cs 去加上忽略路由的設定。

image

 

 

image

https://www.nuget.org/packages/Elmah.MVC

看完了 Elmah.MVC 2.0.2 的設定內容後,再來看看今年七月更新的 2.1.1 與之前的版本有那些不同,

image

相較之前版本多出了兩個設定項目,

IgnoreDefaultRoute」忽略預設路由,原本在 elmah.mvc.route 可以自行修改路由名稱,但 elmah 路徑仍然可以進入,為了要忽略預設的 elmah 路徑,所以要在 RouteConfig.cs 去加入 IgnoreRoute 的設定,現在新版本只要在「elmah.mvc.IgnoreDefaultRoute」設定為 true 就可以忽略 elmah 的預設路徑。

allowedUsers」允許進入 Elmah Dashbord 的使用者名稱,原本只有提供 allowedRoles 的設定項目,但是有很多網站的規模不是很大,所以使用者就沒有群組角色的欄位資料,而現在多了這個 allowedUsers 設定項目就可以使用登入使用者的名稱來決定哪些人可以看 Elmah Dashboard,多位使用者的設定就用逗號分隔。

想要了解詳細的更新內容資訊,可以前往 Elmah.MVC 位於 GitHub 的 Repository「https://github.com/alexanderbeletsky/elmah-mvc」。

 


另外假如你把有安裝 Elmah 的網站佈署到對外開放的主機或是 Windows Azure 時,如果想要觀看這些已經完成佈署網站的 Elmah Dashboard,請務必要將下圖所框起來設定項目做修改,

image

1.「elmah.mvc.requiresAuthentication」設定為 true

2.「elmah.mvc.IgnoreDefaultRoute」設定為 true,忽略預設的 elmah 路徑

3.「elmah.mvc.allowedRoles」如果系統有群組角色的設定,請設定只有部分群組角色可瀏覽 Elmah Dashboard

4.「elmah.mvc.allowedUsers」如果沒有設定群組角色,那麼也請設定只有部分使用者可瀏覽 Elmah Dashboard

5.「elmah.mvc.route」修改進入 Elmah Dashboard 的路徑名稱,不要使用 elmah 或是容易識別的名稱

 

如果你都已經完成上面的設定修改,想要進入線上網站的 Elmah Dashboard 查看時,卻發現以下的訊息:

image

這表示你沒有將 Web.Config 裡 Elmah 區段內的 allowRemoteAccess 設定修改為 true。

將 Elmah 的遠端存取設定給打開(請務必確保上面的設定都有做修改之後才能夠改為 true),如下:

image

 


網站佈署到 Windows Azure 的 WebSite,並使用 SQL Database 來儲存 Elmah Log 記錄,而建立 Elmah_Error Table 與 Stored Procedure 的 Schema 還是用「Elmah on MS SQL Server」提供的 Elmah.SqlServer.sql 來建立,

image

image

如果依然使用這個 Elmah.SqlServer.sql 的 Schema 在 Windows Azure SQL Database 執行的話,就會發現到放在 Windows Azure WebSite 的網站所發生的錯誤都不會將錯誤訊息記錄到 Elmah_Error 這個 Table 裡。

這是因為 SQL Database 與我們所使用的 MS SQL Server or SQL Server Express 有所不同,所以不能夠直接將 Elmah.SqlServer.sql 拿來用在 SQL Database 上,最後我在 stackoverflow 上面找到有人提供修改過的 Schema,將原本在 SQL Database 所建立的 Elmah 相關 Table 與 Stored Procedure 給移除後,執行修改過的 Schema 後,網站的錯誤記錄就可以正常儲存到 Elmah_Error 這個 Table 內,也就可以在 Elmah Dashboard 裡去查看錯誤記錄了。

由 Roberto Bonini 所提供修改過的 Elmah Schema for SQL Database:

--~Changing index [dbo].[ELMAH_Error].PK_ELMAH_Error to a clustered index.  You may    want to pick a different index to cluster on.
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].    [ELMAH_Error]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[ELMAH_Error](
[ErrorId] [uniqueidentifier] NOT NULL,
[Application] [nvarchar](60) NOT NULL,
[Host] [nvarchar](50) NOT NULL,
[Type] [nvarchar](100) NOT NULL,
[Source] [nvarchar](60) NOT NULL,
[Message] [nvarchar](500) NOT NULL,
[User] [nvarchar](50) NOT NULL,
[StatusCode] [int] NOT NULL,
[TimeUtc] [datetime] NOT NULL,
[Sequence] [int] IDENTITY(1,1) NOT NULL,
[AllXml] [nvarchar](max) NOT NULL,
CONSTRAINT [PK_ELMAH_Error] PRIMARY KEY CLUSTERED 
(
[ErrorId] ASC
)WITH (STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF)
)
END
 
IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_Error]') AND name = N'IX_ELMAH_Error_App_Time_Seq')
CREATE NONCLUSTERED INDEX [IX_ELMAH_Error_App_Time_Seq] ON [dbo].[ELMAH_Error] 
(
[Application] ASC,
[TimeUtc] DESC,
[Sequence] DESC
)WITH (STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE  = OFF)
GO
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id =     OBJECT_ID(N'[DF_ELMAH_Error_ErrorId]') AND type = 'D')
BEGIN
ALTER TABLE [dbo].[ELMAH_Error] ADD  CONSTRAINT [DF_ELMAH_Error_ErrorId]  DEFAULT (newid()) FOR [ErrorId]
END
 
GO
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_GetErrorsXml]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[ELMAH_GetErrorsXml]
(
@Application NVARCHAR(60),
@PageIndex INT = 0,
@PageSize INT = 15,
@TotalCount INT OUTPUT
)
AS 
 
SET NOCOUNT ON
 
DECLARE @FirstTimeUTC DATETIME
DECLARE @FirstSequence INT
DECLARE @StartRow INT
DECLARE @StartRowIndex INT
 
SELECT 
    @TotalCount = COUNT(1) 
FROM 
    [ELMAH_Error]
WHERE 
    [Application] = @Application
 
-- Get the ID of the first error for the requested page
 
SET @StartRowIndex = @PageIndex * @PageSize + 1
 
IF @StartRowIndex <= @TotalCount
BEGIN
 
    SET ROWCOUNT @StartRowIndex
 
    SELECT  
        @FirstTimeUTC = [TimeUtc],
        @FirstSequence = [Sequence]
    FROM 
        [ELMAH_Error]
    WHERE   
        [Application] = @Application
    ORDER BY 
        [TimeUtc] DESC, 
        [Sequence] DESC
 
END
ELSE
BEGIN
 
    SET @PageSize = 0
 
END
 
-- Now set the row count to the requested page size and get
-- all records below it for the pertaining application.
 
SET ROWCOUNT @PageSize
 
SELECT 
    errorId     = [ErrorId], 
    application = [Application],
    host        = [Host], 
    type        = [Type],
    source      = [Source],
    message     = [Message],
    [user]      = [User],
    statusCode  = [StatusCode], 
    time        = CONVERT(VARCHAR(50), [TimeUtc], 126) + ''Z''
FROM 
    [ELMAH_Error] error
WHERE
    [Application] = @Application
AND
    [TimeUtc] <= @FirstTimeUTC
AND 
    [Sequence] <= @FirstSequence
ORDER BY
    [TimeUtc] DESC, 
    [Sequence] DESC
FOR
    XML AUTO
 
' 
END
GO
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ELMAH_GetErrorXml]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[ELMAH_GetErrorXml]
(
@Application NVARCHAR(60),
@ErrorId UNIQUEIDENTIFIER
)
AS
 
SET NOCOUNT ON
 
SELECT 
    [AllXml]
FROM 
    [ELMAH_Error]
WHERE
    [ErrorId] = @ErrorId
AND
    [Application] = @Application
 
' 
END
GO
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].    [ELMAH_LogError]') AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[ELMAH_LogError]
(
@ErrorId UNIQUEIDENTIFIER,
@Application NVARCHAR(60),
@Host NVARCHAR(30),
@Type NVARCHAR(100),
@Source NVARCHAR(60),
@Message NVARCHAR(500),
@User NVARCHAR(50),
@AllXml NVARCHAR(MAX),
@StatusCode INT,
@TimeUtc DATETIME
)
AS
 
SET NOCOUNT ON
 
INSERT
INTO
    [ELMAH_Error]
    (
        [ErrorId],
        [Application],
        [Host],
        [Type],
        [Source],
        [Message],
        [User],
        [AllXml],
        [StatusCode],
        [TimeUtc]
    )
VALUES
    (
        @ErrorId,
        @Application,
        @Host,
        @Type,
        @Source,
        @Message,
        @User,
        @AllXml,
        @StatusCode,
        @TimeUtc
    )
 
' 
END
GO

出處:http://stackoverflow.com/questions/15228112/mvc-elmah-and-sql-azure

 

以上

7 則留言:

  1. 必须顶一下,谢谢教程

    回覆刪除
  2. 謝謝你的教學
    我看過了你寫關於ELMAH, 也看過了很多其他的文, 包括原作者
    但我還是有點不太明白怎樣可以在 ELMAH.MVC 2.11 達成用自訂的Controller 去處理
    我要用自訂的原因是因為我有須要自訂Controller 裡面AuthoizeAttribute
    請大大指教一下
    或可否單獨寫一遍是怎樣用ELMAH.MVC 2.11 加 ElmahController
    謝謝你

    回覆刪除
    回覆
    1. 你好,我不清楚你的需求。
      因為你說要在 Elmah.MVC 用自訂的 Controller 去處理,然後這個自訂的 Controller 要使用 AuthorizeAttribute (?)
      .
      我先依照我所理解的來說明,原本的 Elmah 就可以去做到權限管理,在進入 elmah.axd 的時候就會需要去執行權限驗證,
      而 Elmah.Mvc 則是讓我們不再去使用 elmah.axd 進入 elmah dashboard,而是讓我們可以自己定義進入 Elmah.axd 的路徑,這個設定就是在 appSettings 裡的「elmah.mvc.route」,設定這個 route 就可以了,而不必再去自行建立 ElmahController。
      .
      另外使用 Elmah.Mvc 而要讓進入 elmah dashboard 時要做到權限驗證,就需要另外在 elmah.mvc.requiresAuthentication 的設定修改為 true,然後在 elmah.mvc.allowedRoles 或 elmah.mvc.allowedUsers 裡去指定可以進入 dashboard 的角色或使用者。
      .
      詳細資料你可以查看 elmah.mvc 在 Github Repository 的說明原始碼,
      https://github.com/alexanderbeletsky/elmah-mvc
      .
      以上

      刪除
    2. 先謝謝你的回覆

      我其實就是想做權限管理
      但我的網站本身已經有登錄管理

      那如果我可以在controller 裡加上的 attribute , 那我就可以用自己本身的登錄系統作去控制權限
      可以這樣實現嗎?


      另外
      你說的那個 elmah.mvc.allowedRoles 或 elmah.mvc.allowedUsers 是要在 IIS 裡面設定的嗎? 不好意思, 我不太明白
      在例子上看到設定為 Admin , 那這個Admin 是 IIS User 嗎?
      還有 Admin 的 Password 設定呢?


      謝謝你

      刪除
    3. 先回答你的第二個問題,elmah.mvc.allowedRoles 或 elmah.mvc.allowedUsers 的設定是在網站的 Web.Config 內的 AppSettings 裡,這篇文章的一開始(應該說這篇文章有三分之二都是在說明 elmah.mvc 的設定)。

      第一個問題,我猜想你問的是在網站裡的 Controller 要如何使用 AuthorizeAttribute 並結合自己已經做好的登入功能來做到權限控制,內建的 AuthorizeAttribute 已經可以做到,假如你所做的登入功能有使用到 FormsAuthentitcation 的話,就可以做到 Controller 與 Action 的權限控制,在我的其他文章也有介紹過類似的例子,「ASP.NET MVC 實做具有多個角色權限的登入功能 - 使用客製 RoleProvider」
      http://kevintsengtw.blogspot.tw/2013/12/aspnet-mvc-roleprovider.html#.VDPHMPmSweQ
      .
      因為你在這一篇文章裡提問了不同內容的問題,所以讓我無法釐清你的問題點,如果希望單獨詢問某一類型的問題,但是又一時找不到相關的文章來做留言提問的話,其實可以使用左邊的「詢問與建議」功能,這是使用 UserVoice 的服務,並不會把你所提問的內容給公布出來。

      刪除
    4. 如果你是看了「ASP.NET MVC + ELMAH 監控並記錄你的網站錯誤資訊 2」這一篇之後去找尋文中所提及的 ElmahController,
      http://kevintsengtw.blogspot.tw/2011/10/aspnet-mvc-elmah-2.html#.VDQLAPmSzco
      這表示你並沒有我的 Elmah 與 Elmah.MVC 或是 Elmah.MVC 官網的說明給仔細看過,因為在 Elmah.Mvc 後來的版本就把 ElmahController 這個類別給移除了,以前的版本是在安裝了 Elmah.Mvc 之後就會自動建立這個類別,在 Elmah.Mvc 2.0.0 之後就有大幅度的改變,可以看看我的這一篇文章的說明「Elmah.MVC 2.0.0」
      http://kevintsengtw.blogspot.tw/2012/06/elmahmvc-200.html#.VDQLzvmSzco
      .
      再來你所說的登入權限的使用者,並不是網站主機內的帳號或是 IIS User 帳號,如同我前面留言所說的,你只要有在網站裡的登入功能有使用 FormsAuthentication,那麼 Elmah 或 Elmah.Mvc 就可以透過 FormsAuthentication 做權限管理,其實在「ASP.NET MVC + ELMAH 監控並記錄你的網站錯誤資訊 2」就已經有相當詳盡的實作說明了。

      刪除
    5. 謝謝你詳細和細心的回覆
      或者我在UserVoice 的服務再請教你

      刪除