Reputation: 3793
I am planning to create filter based on ActionFilterAttribute
to log user activity:
public class LogAttribute : ActionFilterAttribute
{
public ActionType Type { get; set; }
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
// logic for log
}
}
I would like to use this attribute on Actions in my controllers. I'd like to log some values X,Y,Z
which user is set in my views and also some action related info. In that case I have to probably send these X,Y,Z to my actions as parameters and then in Attribute code get these values from ResultExecutedContext. But this approach confused a little bit me because I will have redundant parameters on each of my actions.
Is there any good approach to share values from View to C# code?
Upvotes: 2
Views: 8002
Reputation: 4638
I am able to achieve this by following the below way
Create a table(name - Portal_Logger
and D_MASTER
)
Next, Created a filter which will track the activity for user/s like below
LoggerActionFilter.cs
using System;
using System.Web.Mvc;
using System.Net;
using System.Linq;
using System.Net.Sockets;
using MyMvcApplication.Models.Entity;
using MyMvcApplication.Utilities;
namespace MyMvcApplication.Filters
{
/// <summary>
///
/// </summary>
public class LoggerActionFilter : ActionFilterAttribute
{
private static TimeZoneInfo INDIAN_ZONE = TimeZoneInfo.FindSystemTimeZoneById("India Standard Time");
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Stores the Request in an Accessible object
var request = filterContext.HttpContext.Request;
var visitCount = Convert.ToInt64(filterContext.HttpContext.Session["LoggerActivityTracking"].ToString());
// Generate an audit
Portal_Logger aLogger = new Portal_Logger()
{
// Your Logger Identifier
LoggerId = Guid.NewGuid(),
//Logged On User Id
LogedUserId = Convert.ToInt32(filterContext.HttpContext.Session["LogedUserID"]),
// The IP Address of the Request
IPAddress = Convert.ToString(ipHostInfo.AddressList.FirstOrDefault(address => address.AddressFamily == AddressFamily.InterNetwork)),
//Get the Web Page name(from the URL that was accessed)
AreaAccessed = UserActivityUtility.GetWebPageName(request.RawUrl == "/"? "/Home/UserLandingPage" : request.RawUrl),
// Creates our Timestamp
Timestamp = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, INDIAN_ZONE),
VisitorSessionId = visitCount
};
// Stores the Logger in the Database
using (dbEntities context = new dbEntities())
{
if (aLogger.LogedUserId != null)
{
aLogger.LogedUserEmpId = context.D_MASTER
.Where(x => x.User_Id == aLogger.LogedUserId)
.Select(x => x.Emp_Id).FirstOrDefault();
aLogger.LogedUserEmpName = context.D_MASTER
.Where(x => x.User_Id == aLogger.LogedUserId)
.Select(x => x.Emp_Name).FirstOrDefault();
aLogger.AccessedType = aLogger.AreaAccessed.Contains("Report") ? "Report" : "Page";
}
context.Portal_Logger.Add(aLogger);
context.SaveChanges();
}
// Finishes executing the Logger as normal
base.OnActionExecuting(filterContext);
}
}
}
Note : Session["LoggerActivityTracking"]
and Session["LogedUserID"]
data i am initializing in a Separate Controller(AuthController
) Action(ValidateUser
) which executes when user logging into the application.
Below i am using a Utility class which will get the exact name for the web page from the RawUrl(eg : /Home/MyController
or /Home/MyController1?id=1&name=chandan
)
Below is my Utility Class
UserActivityUtility.cs
using System;
namespace MyMvcApplication.Utilities
{
public static class UserActivityUtility
{
/// <summary>
///
/// </summary>
/// <param name="pageName"></param>
/// <returns></returns>
public static string GetWebPageName(string pageName)
{
string formattedPageName = string.Empty;
//For All user web pages
if (pageName.Contains("UserLandingPage"))
{
formattedPageName = "Home - App Management";
}
//For Report Web Pages
else if (pageName.Contains("AppUtilization"))
{
formattedPageName = "Application Utilization Report";
}
else if (pageName.Contains("AlertReport"))
{
formattedPageName = "Alert Report";
}
else
{
formattedPageName = pageName;
}
return formattedPageName;
}
}
}
And finally apply that attribute to the Controller Action/s(which actions you want to track) like below
public class HomeController : Controller
{
[LoggerActionFilter(Order = 1)]
public ActionResult UserLandingPage()
{
return View();
}
}
public class AdminController : Controller
{
[LoggerActionFilter(Order = 1)]
public ActionResult CreateUser()
{
return View();
}
}
public class ReportController : Controller
{
[LoggerActionFilter(Order = 1)]
public ActionResult AppUtilization()
{
return View();
}
}
And below are the "MS SQL StoredProcedure" to get the user login details and page wise access summary details
User Login Details
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE Procedure [dbo].[sp_Portal_GetUserLoginDetails_Logger]
(
@FromDate DATE,
@ToDate DATE
)
AS
BEGIN
--EXEC sp_Portal_GetUserLoginDetails_Logger '2017-06-16','2017-06-16'
SET NOCOUNT ON
SELECT u.LogedUserID,u.UserName,u.IPAddress,u.AreaAccessed,u.[Timestamp],u.LastLoggedInDay
FROM
(
SELECT DISTINCT l.LogedUserID,e.Emp_Name as 'UserName',l.IPAddress,l.AreaAccessed,l.[Timestamp],l.LastLoggedInDay
FROM
(
SELECT *
FROM
(
SELECT Row_Number() Over ( PARTITION BY LogedUserID ORDER BY [Timestamp] DESC) As RowNum, LogedUserID,
IPAddress,AreaAccessed,TIMESTAMP, DATEDIFF(D,TIMESTAMP,CURRENT_TIMESTAMP) AS 'LastLoggedInDay'
FROM Portal_Logger
WHERE CAST([Timestamp] AS DATE) BETWEEN @FromDate AND @ToDate
) t2
WHERE RowNum = 1
) l
JOIN D_MASTER e on l.LogedUserID = e.User_Id
) u
ORDER BY u.[Timestamp] DESC
END
Page Wise Access Summary Details
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[sp_Portal_GetUserAccessedSummary_Logger]
(
@FromDate DATE,
@ToDate DATE
)
AS
BEGIN
--EXEC sp_Portal_GetUserAccessedSummary_Logger '2017-06-16','2017-06-16'
SET NOCOUNT ON
SELECT A.AreaAccessed,
COUNT(A.Timestamp) AS NoofTimeAccessed,
CONVERT(TIME(0), DATEADD(SECOND,SUM(SessionSeconds), 0)) AS TotalTimeSpent,
CONVERT(TIME(0), DATEADD(SECOND,AVG(SessionSeconds), 0)) AS AvgTimeSpent,
COUNT(DISTINCT LogedUserID) AS NoofUserAccessed
FROM
(
SELECT
A.AreaAccessed,A.Timestamp,
ISNULL(DATEDIFF(S,A.Timestamp,ISNULL(LEAD(A.Timestamp,1)OVER(PARTITION BY A.VisitorSessionId ORDER BY A.Timestamp),A.Timestamp)),0) AS SessionSeconds,
A.LogedUserID
FROM Portal_Logger A(NOLOCK)
JOIN D_MASTER B(NOLOCK) ON A.LogedUserID=B.User_Id
WHERE A.VisitorSessionId IS NOT NULL
AND CAST(A.Timestamp AS DATE) BETWEEN @FromDate AND @ToDate
)A
GROUP BY A.AreaAccessed
ORDER BY NoofTimeAccessed DESC
SET NOCOUNT OFF
END
Upvotes: 3
Reputation: 16066
what I use is a helper method that use reflection and dumps the model into a dictionary, that way you don't have the issue to copy the values elsewhere, everything ships from a single place "your model"
private static Dictionary<string, string> DumpModel(object obt)
{
Type type = obt.GetType();
if (TypeDefinitionIsList(type))
return null;
PropertyInfo[] properties = obt.GetType().GetProperties();
Func<PropertyInfo, string> func = info =>
{
if (info.PropertyType.GetInterface(typeof (ICatalogue<>).FullName) != null)
{
dynamic propertyValue = info.GetValue(obt, null);
if (propertyValue != null)
return string.Format("{{ Id: {0}, Description: {1} }}",
propertyValue.Id,
propertyValue.Description);
return "null";
}
object normalValue = info.GetValue(obt, null);
if (normalValue == null) return "null";
return TypeDefinitionIsList(normalValue.GetType()) ? HelpersMessages.NotSupportedList : normalValue.ToString();
};
return properties.ToDictionary(x => "model-"+x.Name, func);
}
you can use it in your filter attribute
var request = filterContext.HttpContext.Request;
object model = filterContext.Controller.ViewData.Model;
and you can use something like
DumpModel(model);
use the values to log wharever you need, I hope this helps
Upvotes: 0
Reputation: 19447
The values would have to be set at some point, either by the user or by code. You can however, use the Session
array to contain these values. They will be preserved for the duration of the login session.
To store to session
Session["key"] = "data";
and later in code
var val = Session["key"]; // val now has a value of "data";
Upvotes: 0