I'm trying to setup a session in Active Record for each WCF request. Here is the code:
[WebGet(UriTemplate = "ReadMessages?authUserId={authUserId}&authGuid={authGuid}&userId={userId}&lastMessageId={lastMessageId}&startDate={startDate}&endDate={endDate}")]
public IQueryable<Message> ReadMessages(int authUserId, string authGuid, uint userId, uint lastMessageId,
DateTime startDate, DateTime endDate)
{
UserUtility.Authenticate(authUserId, authGuid);
using (new SessionScope())
{
//get messages.
return from m in MessageData.FindAll(userId, lastMessageId, startDate, endDate)
select ConvertToView(m);
}
}
Even though I have the SessionScope using block, it still gives me a lazy load error because it's returning an IQueryable and so it's converting to a view, which triggers lazy loading, after it's out of the using block I'm guessing. Here is the error:
Initializing[xxx.Business.Schemas.CommonSchemas.Models.Messaging.Message#6575]-failed to lazily initialize a collection of role: xxx.Business.Schemas.CommonSchemas.Models.Messaging.Message.MessageStatusHistories, no session or session was closed
In my configuration, I have IsRunningWebApp as true.
var source = new InPlaceConfigurationSource();
source.IsRunningInWebApp = true;
If you're wondering why I'm returning IQueryable from a WCF web method, it's because I'm using the WCF Web API (http://wcf.codeplex.com/wikipage?title=WCF%20HTTP), which allows you to query your objects in the url querystring using ODATA.
What am I doing wrong? How do I create a session that lives long enough to lazy load the models as I convert them to views on return from the web methods?
I ended up getting it working using this in my Global.asax:
public void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Items.Add("ar.sessionscope", new SessionScope());
}
public void Application_EndRequest(object sender, EventArgs e)
{
try
{
var scope = HttpContext.Current.Items["ar.sessionscope"] as SessionScope;
if (scope != null)
scope.Dispose();
}
catch (Exception ex)
{
HttpContext.Current.Trace.Warn("Error", "EndRequest: " + ex.Message, ex);
}
}
Note: I also had to remove the using(new SessionScope()) from the web method, that will interfere with the solution above.
Related
I have web app with custom exception filter.
public class CustomExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
// do stuff to log exception
}
}
Exception filter is added to filters inside startup class.
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
// ...
options.Filters.Add(new CustomExceptionFilter());
// ...
});
}
}
This custom filter catches almost all non-handled exceptions besides this one.
Microsoft.AspNetCore.Mvc.ViewFeatures.CookieTempDataProvider
The temp data cookie .AspNetCore.Mvc.CookieTempDataProvider could not be loaded.
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Text.Json.JsonHelpers.TryParseDateTimeOffset(ReadOnlySpan`1 source, DateTimeParseData& parseData)
at System.Text.Json.JsonHelpers.TryParseAsISO(ReadOnlySpan`1 source, DateTime& value)
at System.Text.Json.JsonReaderHelper.TryGetEscapedDateTime(ReadOnlySpan`1 source, DateTime& value)
at System.Text.Json.JsonDocument.TryGetValue(Int32 index, DateTime& value)
at System.Text.Json.JsonElement.TryGetDateTime(DateTime& value)
at Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure.DefaultTempDataSerializer.DeserializeDictionary(JsonElement rootElement)
at Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure.DefaultTempDataSerializer.Deserialize(Byte[] value)
at Microsoft.AspNetCore.Mvc.ViewFeatures.CookieTempDataProvider.LoadTempData(HttpContext context)
I'm using TempData to preserve some data between posts and redirects. I've looked at all calls where TempData is used but cannot find the place where this error could show up. This particular error is spat out using Serilog.
My question is why the custom exception filter does not catch this IndexOutOfRangeException? Is there a way to catch them or configure Serilog to be more specific? I would like trace where it comes from to get rid of it.
Follow up
Found similar bug that is described in aspnet core git issues. But my problem is not with some format of string. I get out of range exception even if I check TempData count or Keys.
public static bool HasValue(this ITempDataDictionary tempData, string key)
{
try
{
if (tempData == null)
return false;
// if no tempData is set, it enters here, generates no
// exception, but spits out warning through Serilog.
if (tempData.ContainsKey(key) == false)
return false;
if (tempData.Count == 0)
return false;
return tempData.Peek(key) != null;
}
catch (Exception ex)
{
// ...
}
return false;
}
Temp solution so I can sleep at night
Adding logging override to serilog configuration.
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft.AspNetCore.Mvc.ViewFeatures.CookieTempDataProvider", LogEventLevel.Error);
I'm writing an MVC 4 application that requires the use of multiple NHibernate Session Factories. I'm registering these objects with AutoFac by name. In my global.asax, I need to retrieve all Session Factories and bind/unbind them to the CurrentSessionContext.
protected void Application_BeginRequest(object sender, EventArgs e)
{
foreach (ISessionFactory sessionFactory in SessionFactories)
{
if (!CurrentSessionContext.HasBind(sessionFactory))
{
CurrentSessionContext.Bind(sessionFactory.OpenSession());
}
}
}
protected void Application_EndRequest(object sender, EventArgs e)
{
foreach (ISessionFactory sessionFactory in SessionFactories)
{
ISession session = CurrentSessionContext.Unbind(sessionFactory);
session.Close();
CurrentSessionContext.Unbind(sessionFactory);
}
}
I'm running into a problem retrieving my SessionFactories from AutoFac. Namely, when I try to retrieve them using the Resolve method I get an empty collection. However, things work if I instead use theResolveNamed method:
SessionFactories =
new List<ISessionFactory>
{
container.ResolveNamed<ISessionFactory>("DB1"),
container.ResolveNamed<ISessionFactory>("DB2")
};
While this works, it seems rather fragile (I must update this code if any Name changes, or I add/remove SessionFactories). Is there a way to retrieve all objects from AutoFac by type that includes named instances?
Use Resolve<IEnumerable<ISessionFactory>>()
See Relationship Types for further info.
I am starting to build an app, and I'm planning to use ServiceStack. Just want to know what are the best practices/good approaches for handling NHibernate ISession or, other "per request" context specific session objects.
I thought registering a ISessionFactory in the Ioc like:
container.Register<ISessionFactory>(sessionFactory);
And when needed get a new Session object... Or maybe provide the session object directly:
container.Register<ISession>(c => sessionFactory.OpenSession()).ReusedWithin(ReuseScope.None);
Or either handle the ISession and a default transaction via the Global.asax BeginRequest event:
protected void Application_BeginRequest(object sender, EventArgs e)
{
var session = factory.OpenSession();
ITransaction itrans = session.BeginTransaction();
Context.Items.Add("session", session);
Context.Items.Add("trans", itrans);
}
So, I am kind of lost, what are the best practices, given the above technologies, or similar ones, like EF or another Rest-Services framework?
Thanks in advance
Creating a session per request using a HttpHandler is the most common way of doing it that I have found. Ayende has explained that creating a session is really light weight. http://ayende.com/blog/4123/what-is-the-cost-of-opening-a-session
Ayende actually has series of posts where in he gradually builds out the data access solution. Each post explains why he did what he did and what issues need to be resolved with the steps taken so far. Start here: http://ayende.com/blog/4803/refactoring-toward-frictionless-odorless-code-the-baseline
Finally, http://nhforge.org/blogs/nhibernate/archive/2011/03/03/effective-nhibernate-session-management-for-web-apps.aspx
All of the above are variations of session per request. The common thing across all is not having to manually worry about creating a session/transaction. They will commit/rollback the transactions automatically.
See this blog post for a complete example of how to optimally use ServiceStack and NHibernate together:
http://www.philliphaydon.com/2012/06/using-nhibernate-with-servicestack/
Here's the AppHost example used in the above post:
public class Global : HttpApplication
{
public class SampleServiceAppHost : AppHostBase
{
private readonly IContainerAdapter _containerAdapter;
public SampleServiceAppHost(ISessionFactory sessionFactory)
: base("Service Stack with Fluent NHibernate Sample", typeof(ProductFindService).Assembly)
{
base.Container.Register<ISessionFactory>(sessionFactory);
}
public override void Configure(Funq.Container container)
{
container.Adapter = _containerAdapter;
}
}
void Application_Start(object sender, EventArgs e)
{
var factory = new SessionFactoryManager().CreateSessionFactory();
(new SampleServiceAppHost(factory)).Init();
}
}
I know this is an old question, but I figured I'd go ahead and show anyone who is still interested in an alternate answer how we just did this.
We are using the ServiceRunner in the new ServiceStack API thusly:
public class BaseServiceRunner<TRequest> : ServiceRunner<TRequest>
{
public BaseServiceRunner(AppHost appHost, ActionContext actionContext)
: base(appHost, actionContext) { }
public override void OnBeforeExecute(IRequestContext requestContext, TRequest request)
{
var req = request as MyRequestType;
if(req == null)
base.OnBeforeExecute(requestContext, request);
var factory = TryResolve<NHibernate.ISessionFactory>();
var session = factory.OpenSession();
var trans = session.BeginTransaction(IsolationLevel.ReadCommitted);
requestContext.SetItem("session", session);
requestContext.SetItem("transaction", trans);
}
public override object OnAfterExecute(IRequestContext requestContext, object response)
{
var trans = requestContext.GetItem("transaction") as ITransaction;
if (trans != null && trans.IsActive)
trans.Commit();
var session = requestContext.GetItem("session") as ISession;
if (session != null)
{
session.Flush();
session.Close();
}
return base.OnAfterExecute(requestContext, response);
}
public override object HandleException(IRequestContext requestContext, TRequest request, Exception ex)
{
var req = request as MyRequestType;
if(req != null)
{
var trans = requestContext.GetItem("transaction") as ITransaction;
if (trans != null && trans.IsActive)
trans.Rollback();
var session = requestContext.GetItem("session") as ISession;
if (session != null)
{
session.Flush();
session.Close();
}
}
return base.HandleException(requestContext, request, ex);
}
}
I have created a multi-endpoint WCF service and consumed and it is working fine.
But when I am trying to close the service client then am getting error.
This is how I am creating the client object and disposing its working fine for single endpoint WCF service
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
ICardPrintingService Service = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Service = new CardPrintingServiceClient();
var response = this.Service.GetCardData(new GetCardDataRequest { NIK = 6666620501740003 });
try
{
((CardPrintingServiceClient)Service).Close();
}
catch (Exception ex)
{
MessageBox.Show("error");
}
}
}
}
This is going to the catch block when closing the connection with error message
The remote endpoint no longer recognizes this sequence. This is most
likely due to an abort on the remote endpoint. The value of
wsrm:Identifier is not a known Sequence identifier. The reliable
session was faulted.
Can some one tell me why?
Thanks a ton in adv
Raghavendra
What is the need of casting overhere.
((CardPrintingServiceClient)Service).Close(); //pls explain this.
you can try this in finally block.
if (Service.State != System.ServiceModel.CommunicationState.Closed)
{
Service.Abort();
}
I keep getting the following message while retrieving my domain objects:
failed to lazily initialize a collection of role no session or session was closed
I know the problem has something to do with lazy loading of the collection on my domain object and i'm trying to fix this, but it would be nice if someone could point me in the right direction. The problem is that i have a using statement on my session object and i would like to get rid of the sessions in my repository classes.
Stefan Steinegger recommended to use a TransactionService wich manages the transactions in the following post:
C# Shared Transactions and NHibernate using IRepository
It would be nice someone can provide a tutorial, example on how to implement such a service.
There's a few different ways that you can handle this within a web application and probably the most common in web applications is a Session Per Web Request.
Inside of Application_Start in global.asax.cs, create the SessionFactory and assign it to a static property:
public static ISessionFactory SessionFactory { get; private set; }
protected void Application_Start(object sender, EventArgs e)
{
// your configuration setup
var configuration = new NHibernate.Cfg.Configuration().Configure();
SessionFactory = configuration.BuildSessionFactory();
}
Then, in Application_BeginRequest in global.asax.cs, open a session using the SessionFactory and bind it to the CurrentSessionContext
protected void Application_BeginRequest(object sender, EventArgs e)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
}
and in Application_EndRequest in global.asax.cs, unbind the session and dispose of it
protected void Application_EndRequest(object sender, EventArgs e)
{
var session = CurrentSessionContext.Unbind(SessionFactory);
session.Dispose();
}
Now inside of the application, whenever a session is required, we simply ask the SessionFactory for the current session
public class HomeController : Controller
{
public ActionResult Index()
{
IEnumerable<Product> products = null;
var session = MvcApplication.SessionFactory.GetCurrentSession();
using (var transaction = session.BeginTransaction())
{
// your query, which sounds like it should also eager load
// a child collection
products = session.QueryOver<Product>().List();
transaction.Commit();
}
return View(products);
}
}
There are numerous variations on this, including using a LazySessionContext to lazily create a session only if one is needed for a request, and implementations where ISessionFactory could be injected into you controllers through dependency injection.