Setting bean property from validator - jsf-2

Is there a way to set a bean property from a Validator?
In my case, I have a validator which connects to the database and performs some validation.
Upon successful validation, I want to save the object received from database, inside a bean property.
Currently i'm doing this by setting a static property of my bean from the validator.
Here is my validator method
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
//perform validation
if(isValidated) {
Referral ref = database.getReferral(value.toString()); //receive referral object from batabase
RegistrationBean.staticReferral = ref; // Set ref to RegistrationBean's static property
} else {
FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_FATAL, "Invalid Referral!", "Referral does not exist!");
throw new ValidatorException(msg);
}
}
and here is my RegistrationBean
#ManagedBean
#ViewScoped
public class RegistrationBean implements Serializable {
//other bean properties
private Referral referral;
public static Referral staticReferral;
public RegistrationBean() {
//default constructor
}
public Referral getReferral() {
this.staticReferral = referral;
return referral;
}
// other getters, setters and methods
}
So the questions in my mind are:
Is there a way to set a bean property directly from a bean? (without
using a static property)
Would there be any concurrency issues (one user may receive other user's selected referral object etc) using the existing approach?
Thanks

Static members in managed beans are shared among all instances (and users of your application). So think at least twice before making a member variable static.
If you make your validator a managed bean, you can inject your target managed bean into your validator. See this answer for details.
In the given example an EJB is injected, but you can inject a JSF managed bean via the #ManagedProperty annotation

Related

JSF SelectOneMenu value cannot be user defined type? [duplicate]

I am creating a web application, where you have to read a list of objects / entities from a DB and populate it in a JSF <h:selectOneMenu>. I am unable to code this. Can someone show me how to do it?
I know how to get a List<User> from the DB. What I need to know is, how to populate this list in a <h:selectOneMenu>.
<h:selectOneMenu value="#{bean.name}">
...?
</h:selectOneMenu>
Based on your question history, you're using JSF 2.x. So, here's a JSF 2.x targeted answer. In JSF 1.x you would be forced to wrap item values/labels in ugly SelectItem instances. This is fortunately not needed anymore in JSF 2.x.
Basic example
To answer your question directly, just use <f:selectItems> whose value points to a List<T> property which you preserve from the DB during bean's (post)construction. Here's a basic kickoff example assuming that T actually represents a String.
<h:selectOneMenu value="#{bean.name}">
<f:selectItems value="#{bean.names}" />
</h:selectOneMenu>
with
#ManagedBean
#RequestScoped
public class Bean {
private String name;
private List<String> names;
#EJB
private NameService nameService;
#PostConstruct
public void init() {
names = nameService.list();
}
// ... (getters, setters, etc)
}
Simple as that. Actually, the T's toString() will be used to represent both the dropdown item label and value. So, when you're instead of List<String> using a list of complex objects like List<SomeEntity> and you haven't overridden the class' toString() method, then you would see com.example.SomeEntity#hashcode as item values. See next section how to solve it properly.
Also note that the bean for <f:selectItems> value does not necessarily need to be the same bean as the bean for <h:selectOneMenu> value. This is useful whenever the values are actually applicationwide constants which you just have to load only once during application's startup. You could then just make it a property of an application scoped bean.
<h:selectOneMenu value="#{bean.name}">
<f:selectItems value="#{data.names}" />
</h:selectOneMenu>
Complex objects as available items
Whenever T concerns a complex object (a javabean), such as User which has a String property of name, then you could use the var attribute to get hold of the iteration variable which you in turn can use in itemValue and/or itemLabel attribtues (if you omit the itemLabel, then the label becomes the same as the value).
Example #1:
<h:selectOneMenu value="#{bean.userName}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user.name}" />
</h:selectOneMenu>
with
private String userName;
private List<User> users;
#EJB
private UserService userService;
#PostConstruct
public void init() {
users = userService.list();
}
// ... (getters, setters, etc)
Or when it has a Long property id which you would rather like to set as item value:
Example #2:
<h:selectOneMenu value="#{bean.userId}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user.id}" itemLabel="#{user.name}" />
</h:selectOneMenu>
with
private Long userId;
private List<User> users;
// ... (the same as in previous bean example)
Complex object as selected item
Whenever you would like to set it to a T property in the bean as well and T represents an User, then you would need to bake a custom Converter which converts between User and an unique string representation (which can be the id property). Do note that the itemValue must represent the complex object itself, exactly the type which needs to be set as selection component's value.
<h:selectOneMenu value="#{bean.user}" converter="#{userConverter}">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
with
private User user;
private List<User> users;
// ... (the same as in previous bean example)
and
#ManagedBean
#RequestScoped
public class UserConverter implements Converter {
#EJB
private UserService userService;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
if (submittedValue == null || submittedValue.isEmpty()) {
return null;
}
try {
return userService.find(Long.valueOf(submittedValue));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(String.format("%s is not a valid User ID", submittedValue)), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
if (modelValue == null) {
return "";
}
if (modelValue instanceof User) {
return String.valueOf(((User) modelValue).getId());
} else {
throw new ConverterException(new FacesMessage(String.format("%s is not a valid User", modelValue)), e);
}
}
}
(please note that the Converter is a bit hacky in order to be able to inject an #EJB in a JSF converter; normally one would have annotated it as #FacesConverter(forClass=User.class), but that unfortunately doesn't allow #EJB injections)
Don't forget to make sure that the complex object class has equals() and hashCode() properly implemented, otherwise JSF will during render fail to show preselected item(s), and you'll on submit face Validation Error: Value is not valid.
public class User {
private Long id;
#Override
public boolean equals(Object other) {
return (other != null && getClass() == other.getClass() && id != null)
? id.equals(((User) other).id)
: (other == this);
}
#Override
public int hashCode() {
return (id != null)
? (getClass().hashCode() + id.hashCode())
: super.hashCode();
}
}
Complex objects with a generic converter
Head to this answer: Implement converters for entities with Java Generics.
Complex objects without a custom converter
The JSF utility library OmniFaces offers a special converter out the box which allows you to use complex objects in <h:selectOneMenu> without the need to create a custom converter. The SelectItemsConverter will simply do the conversion based on readily available items in <f:selectItem(s)>.
<h:selectOneMenu value="#{bean.user}" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
See also:
Our <h:selectOneMenu> wiki page
View-Page
<h:selectOneMenu id="selectOneCB" value="#{page.selectedName}">
<f:selectItems value="#{page.names}"/>
</h:selectOneMenu>
Backing-Bean
List<SelectItem> names = new ArrayList<SelectItem>();
//-- Populate list from database
names.add(new SelectItem(valueObject,"label"));
//-- setter/getter accessor methods for list
To display particular selected record, it must be one of the values in the list.
Roll-your-own generic converter for complex objects as selected item
The Balusc gives a very useful overview answer on this subject. But there is one alternative he does not present: The Roll-your-own generic converter that handles complex objects as the selected item. This is very complex to do if you want to handle all cases, but pretty simple for simple cases.
The code below contains an example of such a converter. It works in the same spirit as the OmniFaces SelectItemsConverter as it looks through the children of a component for UISelectItem(s) containing objects. The difference is that it only handles bindings to either simple collections of entity objects, or to strings. It does not handle item groups, collections of SelectItems, arrays and probably a lot of other things.
The entities that the component binds to must implement the IdObject interface. (This could be solved in other way, such as using toString.)
Note that the entities must implement equals in such a way that two entities with the same ID compares equal.
The only thing that you need to do to use it is to specify it as converter on the select component, bind to an entity property and a list of possible entities:
<h:selectOneMenu value="#{bean.user}" converter="selectListConverter">
<f:selectItem itemValue="unselected" itemLabel="Select user..."/>
<f:selectItem itemValue="empty" itemLabel="No user"/>
<f:selectItems value="#{bean.users}" var="user" itemValue="#{user}" itemLabel="#{user.name}" />
</h:selectOneMenu>
Converter:
/**
* A converter for select components (those that have select items as children).
*
* It convertes the selected value string into one of its element entities, thus allowing
* binding to complex objects.
*
* It only handles simple uses of select components, in which the value is a simple list of
* entities. No ItemGroups, arrays or other kinds of values.
*
* Items it binds to can be strings or implementations of the {#link IdObject} interface.
*/
#FacesConverter("selectListConverter")
public class SelectListConverter implements Converter {
public static interface IdObject {
public String getDisplayId();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
return component.getChildren().stream()
.flatMap(child -> getEntriesOfItem(child))
.filter(o -> value.equals(o instanceof IdObject ? ((IdObject) o).getDisplayId() : o))
.findAny().orElse(null);
}
/**
* Gets the values stored in a {#link UISelectItem} or a {#link UISelectItems}.
* For other components returns an empty stream.
*/
private Stream<?> getEntriesOfItem(UIComponent child) {
if (child instanceof UISelectItem) {
UISelectItem item = (UISelectItem) child;
if (!item.isNoSelectionOption()) {
return Stream.of(item.getValue());
}
} else if (child instanceof UISelectItems) {
Object value = ((UISelectItems) child).getValue();
if (value instanceof Collection) {
return ((Collection<?>) value).stream();
} else {
throw new IllegalStateException("Unsupported value of UISelectItems: " + value);
}
}
return Stream.empty();
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) return null;
if (value instanceof String) return (String) value;
if (value instanceof IdObject) return ((IdObject) value).getDisplayId();
throw new IllegalArgumentException("Unexpected value type");
}
}
I'm doing it like this:
Models are ViewScoped
converter:
#Named
#ViewScoped
public class ViewScopedFacesConverter implements Converter, Serializable
{
private static final long serialVersionUID = 1L;
private Map<String, Object> converterMap;
#PostConstruct
void postConstruct(){
converterMap = new HashMap<>();
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object object) {
String selectItemValue = String.valueOf( object.hashCode() );
converterMap.put( selectItemValue, object );
return selectItemValue;
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String selectItemValue){
return converterMap.get(selectItemValue);
}
}
and bind to component with:
<f:converter binding="#{viewScopedFacesConverter}" />
If you will use entity id rather than hashCode you can hit a collision- if you have few lists on one page for different entities (classes) with the same id
Call me lazy but coding a Converter seems like a lot of unnecessary work. I'm using Primefaces and, not having used a plain vanilla JSF2 listbox or dropdown menu before, I just assumed (being lazy) that the widget could handle complex objects, i.e. pass the selected object as is to its corresponding getter/setter like so many other widgets do. I was disappointed to find (after hours of head scratching) that this capability does not exist for this widget type without a Converter. In fact if you supply a setter for the complex object rather than for a String, it fails silently (simply doesn't call the setter, no Exception, no JS error), and I spent a ton of time going through BalusC's excellent troubleshooting tool to find the cause, to no avail since none of those suggestions applied. My conclusion: listbox/menu widget needs adapting that other JSF2 widgets do not. This seems misleading and prone to leading the uninformed developer like myself down a rabbit hole.
In the end I resisted coding a Converter and found through trial and error that if you set the widget value to a complex object, e.g.:
<p:selectOneListbox id="adminEvents" value="#{testBean.selectedEvent}">
... when the user selects an item, the widget can call a String setter for that object, e.g. setSelectedThing(String thingString) {...}, and the String passed is a JSON String representing the Thing object. I can parse it to determine which object was selected. This feels a little like a hack, but less of a hack than a Converter.

How to autowire HttpSessionManager Bean in spring?

so I'm using the spring-session project and I want to know if it is possible to autowire the HttpSessionManager bean? I can see in the users example you are getting it from the request together with the SessionRepository:
HttpSessionManager sessionManager =
(HttpSessionManager) req.getAttribute(HttpSessionManager.class.getName());
SessionRepository<Session> repo =
(SessionRepository<Session>) req.getAttribute(SessionRepository.class.getName());
However, I want to access it from a service near the db layer and because I don't think it is a good design practice to pass the request down to the service I tried to autowire it but it doesn't find a bean of this type. The SessionRepository can be autowired fine because I have defined the bean in my configuration. I also tried to get it using the RequestContextHolder but then the getSessionIds methods always returns empty map, so I end up creating a new session all the time. Here's my whole method:
#Override
public Session getCurrentSession() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
HttpSessionManager sessionManager =
(HttpSessionManager) request.getAttribute(HttpSessionManager.class.getName());
final Map<String, String> sessionIds = sessionManager.getSessionIds(request);
if (sessionIds != null) {
for (Map.Entry<String, String> e : sessionIds.entrySet()) {
final Session session = sessionRepository.getSession(e.getValue());
if (session != null) {
return session;
}
}
}
Session session = sessionRepository.createSession();
sessionRepository.save(session);
return session;
}
My guess is that the RequestContextHolder is capturing the HttpServletRequest before the SessionRepositoryFilter is invoked. That means that the request will not yet be wrapped.
By default the EnableRedisHttpSession configuration does not expose CookieHttpSessionStrategy as a Bean. This is necessary in order to allow users to override the SessionStrategy and supporting older versions of Spring (newer versions of Spring support #Conditional). If you wish to expose CookieHttpSessionStrategy as a Bean, then you can add the following to your configuration:
#Bean
public CookieHttpSessionStrategy sessionStragegy() {
return new CookieHttpSessionStrategy();
}
After thinking about it some I may be able to expose it in future versions. I have created gh-spring-session-75 to address it.

jsf call managed bean method from another managed bean

I am using JSF 2.x in my project.
I have two pages and two managed beans (request scoped) for these two pages.
Page 1 is loaded after user clicks on a link on Home page. This link calls view() method of Bean1 (with request parameter ID=some value) in which we load some data from DB (based on ID) and then redirects to page 1 where this data is displayed.
Later, user navigates from page 1 to page 2 and here we pass the same ID to the page 2.
On page 2, user enters data and clicks on Save button. This will call saveDetails() method of Bean 2.
After the saveDetails() method I want to redirect to page 1 by calling Bean1's view() method and passing the ID as request parameter. I cannot redirect directly to page1 because then there will be no data to display as the bean1 is request scoped.
Hence, I want to call bean1.view() with request parameter ID. I.e. I want to achieve the same behavior as if user has clicked on the link on Home page.
How to achieve this?
Here is the code so far:
#ManagedBean
#Component
#RequestScoped
#Scope("request")
// bean for page1
public class ModifyCDSPageBean extends BasePageBean {
private DisplayTicket ticket;
private String selectedCDS;
...
...
// CDS List
private static Map<String, String> cdsList = new LinkedHashMap<String, String>();
#Autowired
TicketConsoleGTRDao ticketConsoleGTRDao;
private static final Logger LOGGER = Logger.getLogger(ModifyCDSPageBean.class);
public String viewTicketDetails() {
populateCDSList();
....
// Method updated to set DisplayInfoTravail
String id_incident = getRequestParameterValue(TicketConstants.ID_INCIDENT);
List<InfoTravail> travailsList =
ticketConsoleGTRDao.findMatchingTrvailInformation(id_incident);
....
return NavigationConstants.PAGE_MODIFY_CDS;
}
...
...
}
#ManagedBean
#Component
#RequestScoped
#Scope("request")
//Bean for page 2
public class CreateInfoTravailPageBean extends BasePageBean {
private String selectedTypeInfoTravail;
...
...
#Autowired
TicketConsoleGTRDao ticketConsoleGTRDao;
private static final Logger LOGGER = Logger.getLogger(CreateInfoTravailPageBean.class);
public String viewInfoTravail() {
populateTypeInfoTravailList();
...
...
return NavigationConstants.PAGE_CREATE_INFO_TRAVAIL;
}
public String saveInfoTravail() {
String idIncident = getRequestParameterValue(TicketConstants.ID_INCIDENT);
infoTravail.setTicketId(idIncident);
infoTravail.setDate_creation(formatter.format(new Date()));
// HERE I WANT TO CALL ModifyCDSPageBean.viewTicketDetails() method
// pass id_incident as request parameter while making this call
// because if you check ModifyCDSPageBean.viewTicketDetails above it
// looks for request parameter id_incident
}
Re-reading your requirements, it sounds like you want a page initialization code. That is, turn around the flow and don't let entry-points into your page1 call the bean's code, let the bean do that itself:
#ManagedBean #RequestScoped
public class ModifyCDSPageBean {
#Inject #Param(name = TicketConstants.ID_INCIDENT)
private ParamValue<Long> myParam;
#Autowired
private TicketConsoleGTRDao dao;
private List<InfoTravail> travailsList;
#PostConstruct
public void init() {
if (myParam.getValue() != null) {
// do stuff based on the param being set
travailsList = dao.findById(myParam.getValue());
}
}
// getter for travailsList
}
And then include the parameter in your navigation from bean2:
public class Bean2 {
public String save() {
String idIncident = getRequestParameterValue(TicketConstants.ID_INCIDENT);
// do stuff and then return to page1, passing parameter ID_INCIDENT
return String.format("page1?faces-redirect=true&%s=%s",
TicketConstants.ID_INCIDENT, idIncident);
}
If you don't need to execute your view preparation code every time you create ModifyCDSPageBean (ie, if it's used on other pages, too), look into calling it on your page. If you've got JSF-2.2, try <f:viewAction action="#{modifyCDSPageBean.init}"> or on older versions, <f:event listener="#{modifyCDSPageBean.init()}" type="preRenderView">.
Note that #PostConstruct with a #RequestScoped bean will re-create the bean with each AJAX-request, which is probably not what you want. In that case, try #ViewScoped.
My code example uses omnifaces' #Param for my lack of knowledge of spring. Maybe they have something similar in the toolkit already (or just call your getRequestParameterValue from the bean method).

Get only in-service CDI managed beans

My goal is to get a collection of all in-service CDI managed beans (of a certain parent class) from within a JSF2 ExceptionHandlerWrapper. Note the exception handler part is significant because the class is not a valid injection target itself. So my assumption (maybe incorrect) is that my only route is programmatic through BeanManager.
Using BeanManager.getBeans, I can successfully get the set of all beans available for injection. My issue is that when using BeanManager.getReference to obtain the contextual instance of the bean, the bean will be created if it does not already exist. So I am looking for an alternative that will only return instantiated beans. The code below is my starting point
public List<Object> getAllWeldBeans() throws NamingException {
//Get the Weld BeanManager
InitialContext initialContext = new InitialContext();
BeanManager bm = (BeanManager) initialContext.lookup("java:comp/BeanManager");
//List all CDI Managed Beans and their EL-accessible name
Set<Bean<?>> beans = bm.getBeans(AbstractBean.class, new AnnotationLiteral<Any>() {});
List<Object> beanInstances = new ArrayList<Object>();
for (Bean bean : beans) {
CreationalContext cc = bm.createCreationalContext(bean);
//Instantiates bean if not already in-service (undesirable)
Object beanInstance = bm.getReference(bean, bean.getBeanClass(), cc);
beanInstances.add(beanInstance);
}
return beanInstances;
}
Here we are...poking through the javadoc I found Context which has two versions of a get() method for bean instances. One of them, when passing in a creational context, has the same behavior as BeanManager.getReference(). However the other just takes a Bean reference and returns either the contextual instance (if available) or else null.
Leveraging that, here is the version of the original method which returns only instantiated beans:
public List<Object> getAllCDIBeans() throws NamingException {
//Get the BeanManager via JNDI
InitialContext initialContext = new InitialContext();
BeanManager bm = (BeanManager) initialContext.lookup("java:comp/BeanManager");
//Get all CDI Managed Bean types
Set<Bean<?>> beans = bm.getBeans(Object.class, new AnnotationLiteral<Any>() {});
List<Object> beanInstances = new ArrayList<Object>();
for (Bean bean : beans) {
CreationalContext cc = bm.createCreationalContext(bean);
//Get a reference to the Context for the scope of the Bean
Context beanScopeContext = bm.getContext(bean.getScope());
//Get a reference to the instantiated bean, or null if none exists
Object beanInstance = beanScopeContext.get(bean);
if(beanInstance != null){
beanInstances.add(beanInstance);
}
}
return beanInstances;
}

StructureMap IOC/DI and object creation

I'm building small web shop with asp.net mvc and Structuremap ioc/di. My Basket class uses session object for persistence, and I want use SM to create my basket object through IBasket interface. My basket implementation need HttpSessionStateBase (session state wrapper from mvc) in constructor, which is available inside Controller/Action. How do I register my IBasket implementation for SM?
This is my basket interface:
public interface IBasketService {
BasketContent GetBasket();
void AddItem(Product productItem);
void RemoveItem(Guid guid);
}
And SM registration:
ForRequestedType(typeof (IBasketService)).TheDefaultIsConcreteType(typeof (StoreBasketService));
But my StoreBasketService implementation has constructor:
public StoreBasketService(HttpSessionStateBase sessionState)
How do I provide HttpSessionStateBase object to SM, which is available only in controller?
This is my first use of SM IOC/DI, and cann't find solution/example in official documentation and web site ;)
If you absolutely have to have your StoreBasketService use the session, I'd be tempted to define an interface and wrapper around HttpSessionState instead of using HttpSessionStateBase so that you can register it with StructureMap as well.The wrapper would get the session state from the current context. Register the wrapper with StructureMap and then have your StoreBasketService take the interface as the argument to the constructor. Structure map should then know how to create an instance of the interface wrapper and inject it into your StoreBasketService class.
Using an interface and wrapper will allow you to mock the wrapper in your unit tests, muc in the same way HttpSessionStateBase allows mocking the actual session.
public interface IHttpSessionStateWrapper
{
HttpSessionState GetSessionState();
}
public class HttpSessionStateWrapper : IHttpSessionStateWrapper
{
public virtual HttpSessionState GetSessionState()
{
return HttpContext.Current.Session;
}
}
ForRquestedType(typeof(IHttpSessionStateWrapper))
.TheDefaultIsConcreteType(typeof(IHttpSessionStateWrapper));
public class StoreBasketService
{
HttpSessionState session;
public StoreBasketService( IHttpSessionstateWrapper wrapper )
{
session = wrapper.GetSessionState();
}
// basket implementation ...
}
However, you can have StructureMap actually store your basket in the session using .CacheBy(InstanceScope.HttpContext) when registering it. It may actually be better to have your StoreBasketService implement internal storage instead of storing things in the session -- then you lose the dependency on the session state entirely (from the perspective of your class) and your solution could be simpler. Your internal storage could be a Dictionary<Guid,Product> since this is how you access them via your interface.
See also:
http://www.lostechies.com/blogs/chad_myers/archive/2008/07/15/structuremap-basic-scenario-usage.aspx
http://www.lostechies.com/blogs/chad_myers/archive/2008/07/17/structuremap-medium-level-usage-scenarios.aspx
ForRequestedType<IBasketService>()
.TheDefault.Is.OfConcreteType<StoreBasketService>()
.WithCtorArg("sessionState").EqualTo(HttpContext.Current.Session);
?? does that work?
I just started with StructureMap, and I do not get the results you are describing.
I performed a simple test using a simple class, configuring Structuremap to cacheby HttpContext, and from what I can see, CacheBy.HttpContext means within the same request you will get the same instance... not within the same Session
The constructor of my class, sets the date/time in a private field
I have a button which gets 2 instances of MyClass with one second interval...
It then display the time of both instances in a label.
Pressing the first time this button, object A and B are same instance, as their creation time is exactly the same, as expected.
Clicking the button a second time, you would expect the creation time to not have changed if instances would be cached in session... however, in my test I get a new creation time ...
Structuremap configuration:
ObjectFactory.Initialize(x=>x.ForRequestedType<MyClass>(). CacheBy(InstanceScope.HttpContext));
Button clicked event of test page
protected void btnTest_Click(object sender, EventArgs e)
{
MyClass c = ObjectFactory.GetInstance<MyClass>();
System.Threading.Thread.Sleep(1000);
MyClass b = ObjectFactory.GetInstance<MyClass>();
lblResult.Text = String.Format("cache by httpcontext First:{0} Second:{1} session id {2} ", c.GetTimeCreated(), b.GetTimeCreated(),Session.SessionID);
}
MyClass
public class MyClass
{
private DateTime _timeCreated;
public MyClass()
{
_timeCreated = DateTime.Now;
}
public string GetTimeCreated()
{
return _timeCreated.ToString("dd/MM/yyyy hh:mm:ss");
}
}
You could also use one of the ObjectFactory.Inject methods to inject the HttpSessionStateBase into StructureMap. It would then invoke the constructor with the injected HttpSessionStateBase.
I just made my first attempt at creating an custom scope... build a small web application with it, and as far as I can see, it seems to work. This will cache the object inside the current user session and will return the same object as long as you remain inside the same session:
public class HttpSessionBuilder : CacheInterceptor
{
private readonly string _prefix = Guid.NewGuid().ToString();
protected override CacheInterceptor clone()
{
return this;
}
private string getKey(string instanceKey, Type pluginType)
{
return string.Format("{0}:{1}:{2}", pluginType.AssemblyQualifiedName, instanceKey, this._prefix);
}
public static bool HasContext()
{
return (HttpContext.Current.Session != null);
}
protected override bool isCached(string instanceKey, Type pluginType)
{
return HttpContext.Current.Session[this.getKey(instanceKey, pluginType)] != null;
}
protected override object retrieveFromCache(string instanceKey, Type pluginType)
{
return HttpContext.Current.Session[this.getKey(instanceKey, pluginType)];
}
protected override void storeInCache(string instanceKey, Type pluginType, object instance)
{
HttpContext.Current.Session.Add(this.getKey(instanceKey, pluginType), instance);
}
}
You have to configure the ObjectFactory as follows in the global.asax Application_start
ObjectFactory.Initialize(x=>
x.ForRequestedType<MyClass>().InterceptConstructionWith(new HttpSessionBuilder()));

Resources