Automapper with NHibernate - How to persist mapped objects? - asp.net-mvc

I have an ASP.NET MVC application that uses Fluent NHibernate and AutoMapper.
I am primarily using AutoMapper to map my Models to ViewModels and vice versa.
When doing the latter, mapping from my viewmodel back to a model, I am wondering how I can map this to a specific instance from the DB, so when I commit the changes back to the DB (using my NHibernate repository layer, via my service layer), the changes are persisted.
Example:
var advert = Mapper.Map<AdvertViewModel, Advert>(model);
_advertService.UpdateAdvert(advert); // then calls repo which commits current NHibernate trans * Nothing in the DB changes *
If I attempt to commit my NHibernate session, so as to UPDATE this advert in the DB, despite the advert being assigned the correct Key/Id as part of the mapping, I guess because the NHibernate session knows nothing about this advert instance(?) it doesn't write away the changes.
Therefore, I am wondering how to handle this mapping scenario in conjunction with NHibernate?

You could do the following:
// fetch the domain model to update
var domainModelToUpdate = _advertService.Get(viewModel.Id);
// Map the properties that are present in the view model to the domain model
// leaving other properties intact
Mapper.Map<AdvertViewModel, Advert>(viewModel, domainModelToUpdate);
// Update the domain model
_advertService.UpdateAdvert(domainModelToUpdate);
But if the view model already contains everything, you don't need to fetch the domain model before updating. All you have to do is to specify the unsaved-value on your identity column mapping to so that NHibernate knows whether an instance is transient or not and then use SaveOrUpdate:
Id(x => x.ID).WithUnsavedValue(0);
or if you are using nullable integers for your identities pass null.

If it is indeed a session issue - the following singleton might help you out
/// <summary>
/// Handles creation and management of sessions and transactions. It is a singleton because
/// building the initial session factory is very expensive. Inspiration for this class came
/// from Chapter 8 of Hibernate in Action by Bauer and King. Although it is a sealed singleton
/// you can use TypeMock (http://www.typemock.com) for more flexible testing.
/// </summary>
public sealed class NHibernateSessionManager
{
#region Thread-safe, lazy Singleton
System.IO.StreamWriter ConsoleWriter = null;
/// <summary>
/// This is a thread-safe, lazy singleton. See http://www.yoda.arachsys.com/csharp/singleton.html
/// for more details about its implementation.
/// </summary>
public static NHibernateSessionManager Instance
{
get
{
return Nested.NHibernateSessionManager;
}
}
/// <summary>
/// Initializes the NHibernate session factory upon instantiation.
/// </summary>
private NHibernateSessionManager()
{
InitSessionFactory();
}
/// <summary>
/// Assists with ensuring thread-safe, lazy singleton
/// </summary>
private class Nested
{
static Nested() { }
internal static readonly NHibernateSessionManager NHibernateSessionManager =
new NHibernateSessionManager();
}
#endregion
private void InitSessionFactory()
{
// Hold the config var
FluentConfiguration config = Fluently.Configure();
// Set the DB config
MsSqlConfiguration dbConfig = MsSqlConfiguration.MsSql2005.ConnectionString(ConfigurationManager.ConnectionStrings["iSearchConnection"].ConnectionString);
config.Database(dbConfig);
// Load mappings from this assembly
config.Mappings(m => m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly()));
// Create session factory
sessionFactory = config.BuildSessionFactory();
}
/// <summary>
/// Allows you to register an interceptor on a new session. This may not be called if there is already
/// an open session attached to the HttpContext. If you have an interceptor to be used, modify
/// the HttpModule to call this before calling BeginTransaction().
/// </summary>
public void RegisterInterceptor(IInterceptor interceptor)
{
ISession session = ContextSession;
if (session != null && session.IsOpen)
{
throw new CacheException("You cannot register an interceptor once a session has already been opened");
}
GetSession(interceptor);
}
public ISession GetSession()
{
return GetSession(null);
}
/// <summary>
/// Gets a session with or without an interceptor. This method is not called directly; instead,
/// it gets invoked from other public methods.
/// </summary>
private ISession GetSession(IInterceptor interceptor)
{
ISession session = ContextSession;
if (session == null)
{
if (interceptor != null)
{
session = sessionFactory.OpenSession(interceptor);
}
else
{
session = sessionFactory.OpenSession();
}
ContextSession = session;
}
return session;
}
/// <summary>
/// Flushes anything left in the session and closes the connection.
/// </summary>
public void CloseSession()
{
ISession session = ContextSession;
if (session != null && session.IsOpen)
{
session.Flush();
session.Close();
}
if (ConsoleWriter != null)
{
ConsoleWriter.Flush();
ConsoleWriter.Close();
}
ContextSession = null;
}
public void BeginTransaction()
{
ITransaction transaction = ContextTransaction;
if (transaction == null)
{
transaction = GetSession().BeginTransaction();
ContextTransaction = transaction;
}
}
public void CommitTransaction()
{
ITransaction transaction = ContextTransaction;
try
{
if (HasOpenTransaction())
{
transaction.Commit();
ContextTransaction = null;
}
}
catch (HibernateException)
{
RollbackTransaction();
throw;
}
}
public bool HasOpenTransaction()
{
ITransaction transaction = ContextTransaction;
return transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack;
}
public void RollbackTransaction()
{
ITransaction transaction = ContextTransaction;
try
{
if (HasOpenTransaction())
{
transaction.Rollback();
}
ContextTransaction = null;
}
finally
{
CloseSession();
}
}
/// <summary>
/// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms
/// specific <see cref="CallContext" />. Discussion concerning this found at
/// http://forum.springframework.net/showthread.php?t=572.
/// </summary>
private ITransaction ContextTransaction
{
get
{
if (IsInWebContext())
{
return (ITransaction)HttpContext.Current.Items[TRANSACTION_KEY];
}
else
{
return (ITransaction)CallContext.GetData(TRANSACTION_KEY);
}
}
set
{
if (IsInWebContext())
{
HttpContext.Current.Items[TRANSACTION_KEY] = value;
}
else
{
CallContext.SetData(TRANSACTION_KEY, value);
}
}
}
/// <summary>
/// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms
/// specific <see cref="CallContext" />. Discussion concerning this found at
/// http://forum.springframework.net/showthread.php?t=572.
/// </summary>
private ISession ContextSession
{
get
{
if (IsInWebContext())
{
return (ISession)HttpContext.Current.Items[SESSION_KEY];
}
else
{
return (ISession)CallContext.GetData(SESSION_KEY);
}
}
set
{
if (IsInWebContext())
{
HttpContext.Current.Items[SESSION_KEY] = value;
}
else
{
CallContext.SetData(SESSION_KEY, value);
}
}
}
private bool IsInWebContext()
{
return HttpContext.Current != null;
}
private const string TRANSACTION_KEY = "CONTEXT_TRANSACTION";
private const string SESSION_KEY = "CONTEXT_SESSION";
private ISessionFactory sessionFactory;
}
Got this off some NHibernate gurus website - though I can't remember which one - it basically tracks and reconstructs a session for you in whichever app context you are in - works great for my project.
Then you just call the standard methods on the manager:
ISession ctx = NHibernateSessionManager.Instance.GetSession();
try
{
ctx.BeginTransaction();
ctx.Update(entity);
ctx.CommitTransaction();
}
You might have a great session handling already implemented - but from the info, what you are experiencing sounds like a session problem so let me know if that helps

Related

How to make a common class for common code in a controller

I have two controllers both are derivated from a base controller. The code inside them is exactly the same. The only difference is in constructors. Below is my code:
[RoutePrefix("api/v2")]
public class CategoryController : BaseController
{
private IGetTroubleTicketService getTroubleTicketService;
private ICategoryService categoryService;
/// <summary>
/// Constructor for initialization
/// </summary>
public CategoryController()
{
getTroubleTicketService = MethodFactory.Create<IGetTroubleTicketService>();
getTroubleTicketService.SetProvider(new ServiceProvider(Global.Container));
categoryService = MethodFactory.Create<ICategoryService>();
categoryService.SetProvider(new ServiceProvider(Global.Container));
}
/// <summary>
/// Retrieve all Categories
/// </summary>
/// <returns>Categories(Id, Label)</returns>
[HttpGet]
[Route("categoryRef")]
public HttpResponseMessage Categories()
{
try
{
// Validate User Id and Application Id
var user = ValidateUserAndApplication(getTroubleTicketService);
var userLanaguage = Convert.ToInt32(user.Language, CultureInfo.InvariantCulture);
var categories = categoryService.CategoriesData(userLanaguage);
LoggingRequest("categoryRef",null);
response = Request.CreateResponse(HttpStatusCode.OK, categories);
}
catch (Exception exception)
{
//CheckError
CheckError(exception);
}
return response;
}
}
The second one is
[RoutePrefix("api/v2")]
public class ProblemCategoryController : BaseController
{
private IGetTroubleTicketService getTroubleTicketService;
private ICategoryService categoryService;
/// <summary>
/// Constructor for initialization
/// </summary>
public ProblemCategoryController()
{
getTroubleTicketService = MethodFactory.Create<IGetTroubleTicketService>();
getTroubleTicketService.SetProvider(new ServiceProvider(Global.Container));
categoryService = MethodFactory.Create<ICategoryService>();
categoryService.SetProvider(new ServiceProvider(Global.Container));
}
/// <summary>
/// Retrieve all Natures of problem
/// </summary>
/// <returns>Categories(Id, Label)</returns>
[HttpGet]
[Route("problemCategoryRef")]
public HttpResponseMessage ProblemCategories()
{
try
{
// Validate User Id and Application Id
var user = ValidateUserAndApplication(getTroubleTicketService);
var userLanaguage = Convert.ToInt32(user.Language, CultureInfo.InvariantCulture);
var categories = categoryService.CategoriesData(userLanaguage);
LoggingRequest("problemCategoryRef", null);
response = Request.CreateResponse(HttpStatusCode.OK, categories);
}
catch (Exception exception)
{
//CheckError
CheckError(exception);
}
return response;
}
Now as you can see the internal code is exactly the same which I want to avoid creating a Helper class. How can I make this common class for it so as to remove code duplicacy? It's possible without rewrite all the context code to get User and Id app?
I tried this
public class NatureOfProblemHelper : BaseController
{
private IGetTroubleTicketService getTroubleTicketService;
private ICategoryService categoryService;
private string resourceName;
/// <summary>
/// Initializes a new instance of the <see cref="NatureOfProblemHelper"/> class.
/// Constructor for initialization.
/// <param name="resource">Resource requested by user.</param>
/// </summary>
public NatureOfProblemHelper(string resource)
{
getTroubleTicketService = MethodFactory.Create<IGetTroubleTicketService>();
getTroubleTicketService.SetProvider(new ServiceProvider(Global.Container));
categoryService = MethodFactory.Create<ICategoryService>();
categoryService.SetProvider(new ServiceProvider(Global.Container));
resourceName = resource;
}
/// <summary>
/// Retrieve all Natures of problem.
/// </summary>
/// <returns>Categories(Id, Label).</returns>
public HttpResponseMessage GetNaturesOfProblem()
{
// Validate User Id and Application Id
var user = ValidateUserAndApplication(getTroubleTicketService);
var userLanaguage = Convert.ToInt32(user.Language, CultureInfo.InvariantCulture);
var categories = categoryService.CategoriesData(userLanaguage);
LoggingRequest(resourceName, null);
return Request.CreateResponse(HttpStatusCode.OK, categories);
}
And then into each controller
[HttpGet]
[Route("problemCategoryRef")]
public HttpResponseMessage ProblemCategories()
{
try
{
response = natureOfProblem.NaturesOfProblem();
}
catch (Exception exception)
{
//CheckError
CheckError(exception);
}
return response;
}
This build, but I can't get the context that comes from this variable
// Validate User Id and Application Id
var user = ValidateUserAndApplication(getTroubleTicketService);
Why if I put the same lines of code directly in my controller works but if I put in my Helper it doesn't work??

ASP.NET Identity, persistent cookie - is something like this build in?

We are using CookieAuthenticationProvider and would like to implement the 'Remember me' functionality in our application that would work like this:
No matter if the 'Remember me' checkbox is checked or not, the token expiration time should always be set to 30 minutes (with SlidingExpiration turned on)
If user doesn't check 'Remember me' all we do is check if token expired - if it did, then user is redirected to login screen (this is build in into OWIN and works fine)
However if user checks 'Remember me' his credentials should be saved in the additional cookie (with default lifetime of 30 days). If his token expires (the timeout should still be set to 30 minutes), OWIN should use that additional cookie to renew the token automatically in the background. So in other words - if user check 'Remember me' he should be logged in for 30 days or until he logs out.
Question is - how can something like this be done with OWIN? As far as I can see, the default implementation still uses ExpireTimeSpan parameter - the only difference is, that the cookie is marked as persistent, so if user restarts browser he is logged in - but token expiration is still limited by ExpireTimeSpan.
I guess I have to somehow manually save user credentials during the SignIn and override the OnApplyRedirect event (that seems to be the only event fired if an unauthorized user tries to access a view that requires authorization), and instead of redirecting, somehow regenerate user's token... but does anybody know how exactly to do that?
Finally, I ended up writing custom middleware and plugging it in:
RememberMeTokenMiddleware.cs:
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Security;
using WebApplicationtoRemove.Owin.HelperClasses;
using Microsoft.AspNet.Identity.Owin;
namespace WebApplicationtoRemove.Owin.Middleware
{
public class RememberMeTokenMiddleware : OwinMiddleware
{
#region Private Members
private static double RememberMeTokenPeriodOfvalidityInMinutes = 43200;
private IOwinContext Context { get; set; }
#endregion
#region Public Static Members
#endregion
#region Constructor
public RememberMeTokenMiddleware(OwinMiddleware next)
: base(next)
{
}
public RememberMeTokenMiddleware(OwinMiddleware next, double RememberMeTokenPeriodOfvalidityInMinutes)
: base(next)
{
RememberMeTokenMiddleware.RememberMeTokenPeriodOfvalidityInMinutes = RememberMeTokenPeriodOfvalidityInMinutes;
}
#endregion
#region Public Methods
public override async Task Invoke(IOwinContext context)
{
try
{
Context = context;
bool shouldDeleteRememberMeToken = CheckIfRememberMeTokenShouldBeDeleted(context);
if (shouldDeleteRememberMeToken)
{
context.Response.Cookies.Delete("RemoveRememberMeToken");
context.Response.Cookies.Delete("RememberMeToken");
}
else
{
if (context.Authentication.User == null || !context.Authentication.User.Identity.IsAuthenticated)
{
//User is either not set or is not authenticated - try to log him in, using the RememberMeCookie
Login(context);
}
}
}
catch (Exception ex)
{
//Something went wrong - we assume that cookie and/or token was damaged and should be deleted
context.Response.Cookies.Delete("RememberMeToken");
}
await this.Next.Invoke(context);
}
#endregion
#region Static Methods
/// <summary>
/// Check conditions and creates RememberMeToken cookie if necessary. This should be called inside SidnedIn event of CookieProvider
/// </summary>
public static void CheckAndCreateRememberMeToken(CookieResponseSignedInContext ctx)
{
try
{
bool signedInFromRememberMeToken = CheckIfUserWasSignedInFromRememberMeToken(ctx.OwinContext);
if (!signedInFromRememberMeToken && ctx.Properties.IsPersistent)
{
//Login occured using 'normal' path and IsPersistant was set - generate RememberMeToken cookie
var claimsToAdd = GenerateSerializableClaimListFromIdentity(ctx.Identity);
SerializableClaim cookieExpirationDate = GenerateRememberMeTokenExpirationDateClaim();
claimsToAdd.Add(cookieExpirationDate);
var allClaimsInFinalCompressedAndProtectedBase64Token = GenerateProtectedAndBase64EncodedClaimsToken(claimsToAdd);
ctx.Response.Cookies.Append("RememberMeToken", allClaimsInFinalCompressedAndProtectedBase64Token, new CookieOptions()
{
Expires = DateTime.Now.AddMinutes(RememberMeTokenPeriodOfvalidityInMinutes)
});
//Remove the SignedInFromRememberMeToken cookie, to let the middleware know, that user was signed in using normal path
ctx.OwinContext.Set("SignedInFromRememberMeToken", false);
}
}
catch (Exception ex)
{
//Log errors using your favorite logger here
}
}
/// <summary>
/// User logged out - sets information (using cookie) for RememberMeTokenMiddleware that RememberMeToken should be removed
/// </summary>
public static void Logout(IOwinContext ctx)
{
ctx.Response.Cookies.Append("RemoveRememberMeToken", "");
}
#endregion
#region Private Methods
/// <summary>
/// Returns information if user was signed in from RememberMeToken cookie - this information should be used to determine if RememberMeToken lifetime should be regenerated or not (it should be, if user signed in using normal path)
/// </summary>
private static bool CheckIfUserWasSignedInFromRememberMeToken(IOwinContext ctx)
{
bool signedInFromRememberMeToken = ctx.Get<bool>("SignedInFromRememberMeToken");
return signedInFromRememberMeToken;
}
/// <summary>
/// Generates serializable collection of user claims, that will be saved inside the cookie token. Custom class is used because Claim class causes 'Circular Reference Exception.'
/// </summary>
private static List<SerializableClaim> GenerateSerializableClaimListFromIdentity(ClaimsIdentity identity)
{
var dataToReturn = identity.Claims.Select(x =>
new SerializableClaim()
{
Type = x.Type,
ValueType = x.ValueType,
Value = x.Value
}).ToList();
return dataToReturn;
}
/// <summary>
/// Generates a special claim containing an expiration date of RememberMeToken cookie. This is necessary because we CANNOT rely on browsers here - since each one threat cookies differently
/// </summary>
private static SerializableClaim GenerateRememberMeTokenExpirationDateClaim()
{
SerializableClaim cookieExpirationDate = new SerializableClaim()
{
Type = "RememberMeTokenExpirationDate",
Value = DateTime.Now.AddMinutes(RememberMeTokenPeriodOfvalidityInMinutes).ToBinary().ToString()
};
return cookieExpirationDate;
}
/// <summary>
/// Generates token containing user claims. The token is compressed, encrypted using machine key and returned as base64 string - this string will be saved inside RememberMeToken cookie
/// </summary>
private static string GenerateProtectedAndBase64EncodedClaimsToken(List<SerializableClaim> claimsToAdd)
{
var allClaimsAsString = JsonConvert.SerializeObject(claimsToAdd);
var allClaimsAsBytes = Encoding.UTF8.GetBytes(allClaimsAsString);
var allClaimsAsCompressedBytes = CompressionHelper.CompressDeflate(allClaimsAsBytes);
var allClaimsAsCompressedBytesProtected = MachineKey.Protect(allClaimsAsCompressedBytes, "RememberMeToken");
var allClaimsInFinalCompressedAndProtectedBase64Token = Convert.ToBase64String(allClaimsAsCompressedBytesProtected);
return allClaimsInFinalCompressedAndProtectedBase64Token;
}
/// <summary>
/// Primary login method
/// </summary>
private void Login(IOwinContext context)
{
var base64ProtectedCompressedRememberMeTokenBytes = context.Request.Cookies["RememberMeToken"];
if (!string.IsNullOrEmpty(base64ProtectedCompressedRememberMeTokenBytes))
{
var RememberMeToken = GetRememberMeTokenFromData(base64ProtectedCompressedRememberMeTokenBytes);
var claims = JsonConvert.DeserializeObject<IEnumerable<SerializableClaim>>(RememberMeToken);
bool isRememberMeTokenStillValid = IsRememberMeTokenStillValid(claims);
if (isRememberMeTokenStillValid)
{
//Token is still valid - sign in
SignInUser(context, claims);
//We set information that user was signed in using the RememberMeToken cookie
context.Set("SignedInFromRememberMeToken", true);
}
else
{
//Token is invalid or expired - we remove unnecessary cookie
context.Response.Cookies.Delete("RememberMeToken");
}
}
}
/// <summary>
/// We log user, using passed claims
/// </summary>
private void SignInUser(IOwinContext context, IEnumerable<SerializableClaim> claims)
{
List<Claim> claimList = new List<Claim>();
foreach (var item in claims)
{
string type = item.Type;
string value = item.Value;
claimList.Add(new Claim(type, value));
}
ClaimsIdentity ci = new ClaimsIdentity(claimList, DefaultAuthenticationTypes.ApplicationCookie);
context.Authentication.SignIn(ci);
context.Authentication.User = context.Authentication.AuthenticationResponseGrant.Principal;
}
/// <summary>
/// Get information if RememberMeToken cookie is still valid (checks not only the date, but also some additional information)
/// </summary>
private bool IsRememberMeTokenStillValid(IEnumerable<SerializableClaim> claims)
{
var userIdClaim = claims.Where(x => x.Type == ClaimTypes.NameIdentifier).SingleOrDefault();
if (userIdClaim == null)
{
throw new Exception("RememberMeTokenAuthMiddleware. Claim of type NameIdentifier was not found.");
}
var userSecurityStampClaim = claims.Where(x => x.Type == "AspNet.Identity.SecurityStamp").SingleOrDefault();
if (userSecurityStampClaim == null)
{
throw new Exception("RememberMeTokenAuthMiddleware. Claim of type SecurityStamp was not found.");
}
string userId = userIdClaim.Value;
var userManager = Context.GetUserManager<ApplicationUserManager>();
if (userManager == null)
{
throw new Exception("RememberMeTokenAuthMiddleware. Unable to get UserManager");
}
var currentUserData = userManager.FindById(userId);
if (currentUserData == null)
{
return false;
}
if (currentUserData.LockoutEndDateUtc >= DateTime.Now)
{
return false;
}
if (currentUserData.SecurityStamp != userSecurityStampClaim.Value)
{
//User Securitystamp was changed
return false;
}
return GetRememberMeTokenExpirationMinutesLeft(claims) > 0;
}
/// <summary>
/// Returns how many minutes the RememberMeToken will be valid - if it expired, returns zero or negative value
/// </summary>
private double GetRememberMeTokenExpirationMinutesLeft(IEnumerable<SerializableClaim> claims)
{
double dataToReturn = -1;
var RememberMeTokenExpirationDate = GetRememberMeTokenExpirationDate(claims);
dataToReturn = (RememberMeTokenExpirationDate - DateTime.Now).TotalMinutes;
return dataToReturn;
}
/// <summary>
/// Returns a DateTime object containing the expiration date of the RememberMeToken
/// </summary>
private DateTime GetRememberMeTokenExpirationDate(IEnumerable<SerializableClaim> claims)
{
DateTime RememberMeTokenExpirationDate = DateTime.Now.AddDays(-1);
var RememberMeTokenExpirationClaim = GetRememberMeTokenExpirationDateClaim(claims);
if (RememberMeTokenExpirationClaim == null)
{
throw new Exception("RememberMeTokenAuthMiddleware. RememberMeTokenExpirationClaim was not found.");
}
long binaryTime = Convert.ToInt64(RememberMeTokenExpirationClaim.Value);
RememberMeTokenExpirationDate = DateTime.FromBinary(binaryTime);
return RememberMeTokenExpirationDate;
}
/// <summary>
/// Returns the claim determining the expiration date of the token
/// </summary>
private SerializableClaim GetRememberMeTokenExpirationDateClaim(IEnumerable<SerializableClaim> claims)
{
var RememberMeTokenExpirationClaim = claims.Where(x => x.Type == "RememberMeTokenExpirationDate").SingleOrDefault();
return RememberMeTokenExpirationClaim;
}
/// <summary>
/// Attempts to decipher the RememberMeToken to the JSON format containing claims
/// </summary>
private string GetRememberMeTokenFromData(string base64ProtectedCompressedRememberMeTokenBytes)
{
var protectedCompressedRememberMeTokenBytes = Convert.FromBase64String(base64ProtectedCompressedRememberMeTokenBytes);
var compressedRememberMeTokenBytes = MachineKey.Unprotect(protectedCompressedRememberMeTokenBytes, "RememberMeToken");
var RememberMeTokenBytes = CompressionHelper.DecompressDeflate(compressedRememberMeTokenBytes);
var RememberMeToken = Encoding.UTF8.GetString(RememberMeTokenBytes);
return RememberMeToken;
}
/// <summary>
/// Returns information if token cookie should be delated (for example, when user click 'Logout')
/// </summary>
private bool CheckIfRememberMeTokenShouldBeDeleted(IOwinContext context)
{
bool shouldDeleteRememberMeToken = (context.Request.Cookies.Where(x => x.Key == "RemoveRememberMeToken").Count() > 0);
return shouldDeleteRememberMeToken;
}
#endregion
}
}
And some helper classes:
CompressionHelper.cs:
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Web;
namespace WebApplicationtoRemove.Owin.HelperClasses
{
/// <summary>
/// Data compression helper
/// </summary>
public static class CompressionHelper
{
public static byte[] CompressDeflate(byte[] data)
{
MemoryStream output = new MemoryStream();
using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Optimal))
{
dstream.Write(data, 0, data.Length);
}
return output.ToArray();
}
public static byte[] DecompressDeflate(byte[] data)
{
MemoryStream input = new MemoryStream(data);
MemoryStream output = new MemoryStream();
using (DeflateStream dstream = new DeflateStream(input, CompressionMode.Decompress))
{
dstream.CopyTo(output);
}
return output.ToArray();
}
}
}
SerializableClaim.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApplicationtoRemove.Owin.HelperClasses
{
public class SerializableClaim
{
public string Type { get; set; }
public string ValueType { get; set; }
public string Value { get; set; }
}
}
To test the above - create new MVC 4.6.x project (authentication mode: Individual User Accounts), add the above classes to it and then modify the Startup.Auth.cs:
using System;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Google;
using Owin;
using WebApplicationtoRemove.Models;
using WebApplicationtoRemove.Owin.Middleware;
namespace WebApplicationtoRemove
{
public partial class Startup
{
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),
OnResponseSignedIn = ctx =>
{
RememberMeTokenMiddleware.CheckAndCreateRememberMeToken(ctx);
},
OnResponseSignOut = ctx =>
{
RememberMeTokenMiddleware.Logout(ctx.OwinContext);
}
}
});
app.Use<RememberMeTokenMiddleware>();
}
}
}
What interests you there are these:
OnResponseSignedIn = ctx =>
{
RememberMeTokenMiddleware.CheckAndCreateRememberMeToken(ctx);
},
OnResponseSignOut = ctx =>
{
RememberMeTokenMiddleware.Logout(ctx.OwinContext);
}
and this line:
app.Use<RememberMeTokenMiddleware>();
This should enable the middleware. How this works: if the user checks 'Remember me' checkbox, a RememberMeToken cookie will be created (containing all the claims user had during login) alongside the 'AspNet.ApplicationCookie'.
When the session times out, the middleware will check if the RememberMeToken exists, and is still valid - if so: it will log in the user seamlessly in background.
Hope this helps anyone.

get current culture or browser locale on mvc 4

How do you get current culture or browser locale on MVC 4.
I find some samples which gets it from HttpContext and HttpRequest but this doesnt work on MVC 4.
How do you do it on MVC 4?
I find some samples which gets it from HttpContext and HttpRequest but this doesnt work on MVC 4.
I just love the it doesn't work problem description!!! It's like saying to a mechanic whom you don't want to pay for the job: my car doesn't work, tell me what is wrong with it so that I can fix it myself, without showing him your car of course.
Anyway, you still got the HttpRequest in your controller action. Look at the UserLanguages property:
public ActionResult SomeAction()
{
string[] userLanguages = Request.UserLanguages;
...
}
Remark: the value of this property will be null if the user agent didn't send the Accept-Languages request header. So make sure you check whether it is not null before accessing its value to avoid getting NREs.
We use the following code in NuGetGallery
/// <summary>
/// Extensions on <see cref="HttpRequest"/> and <see cref="HttpRequestBase"/>
/// </summary>
public static class HttpRequestExtensions
{
/// <summary>
/// Retrieve culture of client.
/// </summary>
/// <param name="request">Current request.</param>
/// <returns><c>null</c> if not to be determined.</returns>
public static CultureInfo DetermineClientCulture(this HttpRequest request)
{
return DetermineClientCulture(new HttpRequestWrapper(request));
}
/// <summary>
/// Retrieve culture of client.
/// </summary>
/// <param name="request">Current request.</param>
/// <returns><c>null</c> if not to be determined.</returns>
public static CultureInfo DetermineClientCulture(this HttpRequestBase request)
{
if (request == null)
{
return null;
}
string[] languages = request.UserLanguages;
if (languages == null)
{
return null;
}
//first try parse of full langcodes. Stop with first success.
foreach (string language in languages)
{
string lang = language.ToLowerInvariant().Trim();
try
{
return CultureInfo.GetCultureInfo(lang);
}
catch (CultureNotFoundException)
{
}
}
//try parse again with first 2 chars. Stop with first success.
foreach (string language in languages)
{
string lang = language.ToLowerInvariant().Trim();
if (lang.Length > 2)
{
string lang2 = lang.Substring(0, 2);
try
{
return CultureInfo.GetCultureInfo(lang2);
}
catch (CultureNotFoundException)
{
}
}
}
return null;
}
}
usage:
/// <summary>
/// Called before the action method is invoked.
/// </summary>
/// <param name="filterContext">Information about the current request and action.</param>
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.IsChildAction)
{
//no need to do the hassle for a child action
//set the culture from the request headers
var clientCulture = Request.DetermineClientCulture();
if (clientCulture != null)
{
//and/or CurrentUICulture
Thread.CurrentThread.CurrentCulture = clientCulture;
}
}
base.OnActionExecuting(filterContext);
}

WCF services, loading data from database

I have some problem with WCF services in asp.net mvc application.
Help me please!
When I run my project some data for some entities are loading from database, but some data are not loading(Internal server error).
Code and description of exceptions:
EntityTypeController:
public HttpResponseMessage GetAll()
{
//for debug
var t = EntityTypeService.GetAll();
//some exception here!
/*
The request channel timed out while waiting for a reply after 00:00:59.9979999.
Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding.
The time allotted to this operation may have been a portion of a longer timeout.
*/
//SendTimeout is 00:30:00 for all services.
or
/*The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.*/
or
/*An error occurred while receiving the HTTP response to http://localhost:18822/Services/Department.svc.
This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an
HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details.*/
return CreateResponse<IEnumerable<EntityType>>(EntityTypeService.GetAll);
}
EntityTypeService:
public class EntityTypeService : BaseService<Base.Entities.EntityType>, IEntityTypeService
{
public IQueryable<Base.Entities.EntityType> GetAll()
{
var t = Repository.Get();
return t;
}
}
Repository:
public class Repository<TEntity>: IRepository<TEntity> where TEntity: BaseEntity
{
[Inject]
public IDataContext Context { get; set; }
[Inject]
public IDatabase DataSource { get; set; }
/// <summary>
/// Get entities from repository
/// </summary>
/// <param name="filter">Filter</param>
/// <param name="orderBy">Order by property</param>
/// <param name="includeProperties"></param>
/// <returns></returns>
public virtual IQueryable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
{
var query = Context.Set<TEntity>().AsQueryable();
// Apply filter
filter.Do((s) => {
query = query.Where(filter);
});
// Apply includes
foreach (var includeProperty in includeProperties.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
// Apply order by if need
var result = orderBy
.With(x => x(query))
.Return(x => x, query);
return result;
}
/// <summary>
/// Find entity by key
/// </summary>
/// <param name="id">Identificator</param>
/// <returns>Entity</returns>
public virtual TEntity GetById(object id)
{
//throw new NotImplementedException();
return Context.Set<TEntity>().Find(id);
}
/// <summary>
/// Add new entity to repository
/// </summary>
/// <param name="entity">New entity</param>
public virtual void Insert(TEntity entity)
{
AttachIsNotAttached(entity);
Context.Set<TEntity>().Add(entity);
Context.SaveChanges();
}
/// <summary>
/// Updated entity in repositoy
/// </summary>
/// <param name="entity">Entity</param>
public virtual void Update(TEntity entity)
{
AttachIsNotAttached(entity);
Context.Entry(entity).State = EntityState.Modified;
Context.SaveChanges();
}
/// <summary>
/// Remove entity from rpository
/// </summary>
/// <param name="entity">Entity</param>
public virtual void Delete(TEntity entity)
{
AttachIsNotAttached(entity);
Context.Set<TEntity>().Remove(entity);
Context.Entry(entity).State = EntityState.Deleted;
Context.SaveChanges();
}
/// <summary>
/// Return models error
/// </summary>
/// <returns></returns>
public IEnumerable<object> GetValidationModelErrors()
{
return Context.GetValidationErrors();
}
/// <summary>
/// Attach entity
/// </summary>
/// <param name="entity"></param>
protected void AttachIsNotAttached(TEntity entity)
{
if (Context.Entry(entity).State == EntityState.Detached)
{
var attached = Context.Set<TEntity>().Local.Where(x => x.Id == entity.Id).FirstOrDefault();
attached.Do((s) => {
Context.Entry(s).State = EntityState.Detached;
});
Context.Set<TEntity>().Attach(entity);
}
}
}
By default the wcf config has many limitations like the max size of array, and the exception doesnt exactly what happens,maybe in your case it's due to the default wcf config, I advice you to use svcTraceViewer, it will help you a lot to understand what happen exactly with an explicit messages.

Creating WindsorViewPageActivator

I was playing with new Asp net mvc 3 RC2. I have created a WindsorViewPageActivator class as follows
public class WindsorViewPageActivator : IViewPageActivator
{
object IViewPageActivator.Create(ControllerContext controllerContext, Type type)
{
return DependencyResolver.Current.GetService(type);
}
}
and then a WindsorDependencyResolver class
public class WindsorDependencyResolver : IDependencyResolver
{
private readonly IWindsorContainer container;
public WindsorDependencyResolver(IWindsorContainer container)
{
this.container = container;
}
#region IDependencyResolver Members
public object GetService(Type serviceType)
{
return Resolve(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return container.ResolveAll(serviceType).Cast<object>();
}
public IEnumerable<TService> GetAllInstances<TService>()
{
return container.ResolveAll<TService>();
}
public TService GetInstance<TService>()
{
return (TService)Resolve(typeof(TService));
}
#endregion
private object Resolve(Type serviceType)
{
try
{
return container.Resolve( serviceType);
}
catch (Exception ex)
{
return null;
}
}
}
}
Now I am doing in Global.asax something like this
container.Register(Component.For<IControllerActivator>().ImplementedBy<WindsorControllerActivator>());
container.Register(Component.For<IViewPageActivator>().ImplementedBy<WindsorViewPageActivator>());
container.Register(Component.For<IControllerFactory>().ImplementedBy<DefaultControllerFactory>());
DependencyResolver.SetResolver (new WindsorDependencyResolver(container));
'
Now I am getting the following error
The view found at '~/Views/Account/LogOn.cshtml' was not created.
Do I need to register each view page in windsor container If yes then how can I register each view. I am using Razor view engine.
Thanks
Yes, in order to resolve stuff you need to register it. Have a look at the documentation to familiarise yourself with the API.
I have tried this myself, and unfortunately I can't seem to get it to work properly. I do the following in my solution:
public class WindsorViewPageActivator : IViewPageActivator
{
private readonly IKernel _kernel;
/// <summary>
/// Initializes a new instance of the <see cref="WindsorViewPageActivator"/> class.
/// </summary>
/// <param name="kernel">The kernel.</param>
public WindsorViewPageActivator([NotNull] IKernel kernel)
{
if (kernel == null) throw new ArgumentNullException("kernel");
_kernel = kernel;
}
public object Create(ControllerContext controllerContext, Type type)
{
if (!_kernel.HasComponent(type))
{
if (IsSupportedView(type))
{
_kernel.Register(Component.For(type).LifestylePerWebRequest());
}
else
{
return Activator.CreateInstance(type);
}
}
var viewPageInstance = _kernel.Resolve(type);
return viewPageInstance;
}
/// <summary>
/// Determines whether the specified type is of a supported view type.
/// </summary>
/// <param name="viewType">Type of the service.</param>
/// <returns>
/// <c>true</c> if the specified type is of a supported view type; otherwise, <c>false</c>.
/// </returns>
private static bool IsSupportedView(Type viewType)
{
return viewType.IsAssignableTo<WebViewPage>()
|| viewType.IsAssignableTo<ViewPage>()
|| viewType.IsAssignableTo<ViewMasterPage>()
|| viewType.IsAssignableTo<ViewUserControl>()
;
}
}
This works as long as you don't change anything in your markup. If you do, you'll get a registration error, as the view now will generate a new type, that doesn't exist in the container (but it has the same name!).
What I thought of doing, was to aggressively release the view component as soon as it's resolved, but I can't get rid of it in the container for some reason. Not even an explicit call to _kernel.ReleaseComponent(viewPageInstance) would do it (but that's of course just releasing the instance, not the type).
What we really need to do, is make Windsor inject properties into existing instances. That is, use Activator.CreateInstance(type) and then tell Windsor to inject the properties into the instance. But Windsor doesn't support injecting properties into existing instances, so we need to hack something together, that will do that for us.
I've seen this one http://www.jeremyskinner.co.uk/2008/11/08/dependency-injection-with-aspnet-mvc-action-filters/ (at the bottom), but that wouldn't perform very well.
My solution was simply to manually set my properties in the viewpage activator (you have a base viewpage type), but maybe there's some better solution?
EDIT
I managed to get it working after all!
My solution is to simply create a custom component activator and mimic what's being done in the MVC framework, like so:
public class ViewPageComponentActivator : DefaultComponentActivator
{
public ViewPageComponentActivator(ComponentModel model, IKernel kernel, ComponentInstanceDelegate onCreation, ComponentInstanceDelegate onDestruction)
: base(model, kernel, onCreation, onDestruction)
{
}
protected override object CreateInstance(CreationContext context, ConstructorCandidate constructor, object[] arguments)
{
// Do like the MVC framework.
var instance = Activator.CreateInstance(context.RequestedType);
return instance;
}
}
The component activator simply always return a new instance of the view. Because the component is registered as being transient, CreateInstanceis always called. There might be some tweaking possibilities here.
The viewpage activator is now much simpler. Note that the type of service is different whenever you change the view, so we must register the type based on it's unique name (I haven't tweaked this yet, but there might be a nicer way to name the component).
/// <summary>
/// An activator using Castle Kernel for activating views.
/// </summary>
public class WindsorViewPageActivator : IViewPageActivator
{
private readonly IKernel _kernel;
/// <summary>
/// Initializes a new instance of the <see cref="WindsorViewPageActivator"/> class.
/// </summary>
/// <param name="kernel">The kernel.</param>
public WindsorViewPageActivator([NotNull] IKernel kernel)
{
if (kernel == null) throw new ArgumentNullException("kernel");
_kernel = kernel;
}
public object Create(ControllerContext controllerContext, Type type)
{
if (!_kernel.HasComponent(type.FullName))
{
_kernel.Register(Component.For(type).Named(type.FullName).Activator<ViewPageComponentActivator>().LifestyleTransient());
}
return _kernel.Resolve(type.FullName, type);
}
}
I hope this might be of use to someone in similar situations.

Resources