I am using Orchard and would like to cache data specific to an authenticated user.
When a new user logs in, or after a period of time, the database should be queried again.
I've accomplished half of this below (after 30 minutes it will query the database again):
private UserData SomeUserSpecificData()
{
var data = _cacheManager.Get("userdata",
ctx => {
ctx.Monitor(_clock.When(TimeSpan.FromMinutes(30)));
return GetDatabaseData();
});
return data;
}
But how would I force the cache to re-query the database when a new user has logged in?
I have a feeling it might involve ISignals but not sure how to implement this.
Thanks.
You're absolutely right with ISignals!
You just need to inject ISignals and use it like this:
private readonly Orchard.Caching.ISignals _signals;
private UserData SomeUserSpecificData()
{
var data = _cacheManager.Get("userdata",
ctx => {
ctx.Monitor(_clock.When(TimeSpan.FromMinutes(30)));
cts.Monitor(_signals.When("SOMETHING_HAPPENED_FOR_xyz"));
return GetDatabaseData();
});
return data;
}
What's also nice to know: You can monitor as many signals as you want.
To keep things clean I'll typically create a static class like this:
public static class CacheSignals
{
public const string SomethingHappened = "SOMETHING_HAPPENED";
public const string SomethingHappenedForUser(int userId) => string.Format("SOMETHING_HAPPENED_FOR_{0}", userId);
}
Now you can simply implement a custom EventHandler forOrchard.Users.Events.IUserEventHandler
and invalidate the cache on LoggedIn.
private readonly Orchard.Caching.ISignals _signals;
public void LoggedIn(Security.IUser user)
{
_signals.Trigger(CacheSignals.SomethingHappenedForUser(user.Id));
}
Related
I am exposing my repository operations through web api. Repository has been implemented with Entity framework and Unit Of Work Pattern. I have many instances of the same database. Each one represent the data of a different Client. Now the issue is how can I set the connection string dynamically through each webapi call? Should I get connection string parameter with each call ? Or I should host web Api per client ?
Based on the information provided, I would use the same controller and look up the connection string rather than rather than hosting separate Web API instances for each client. There would be more complexity in hosting multiple instances and given the only difference indicated is the connection string, I do not think the complexity would be justified.
The first thing we will need to do is determine which client is calling in order to get the appropriate connection string. This could be done with tokens, headers, request data, or routing. Routing is simplest and most generally accessible to clients, so I will demonstrate using it; however, carefully consider your requirements in deciding how you will make the determination.
[Route( "{clientId}" )]
public Foo Get( string clientId ) { /* ... */ }
Next we need to get the right DbContext for the client. We want to keep using DI but that is complicated in that we do not know until after the Controller is created what connection string is needed to construct the object. Therefore, we need to inject some form of factory rather than the object itself. In this case we will represent this as a Func<string, IUnitOfWork> with the understanding it takes the 'clientId' as a string and returns an appropriately instantiated IUnitOfWork. We could alternatively use a named interface for this.
[RoutePrefix("foo")]
public class FooController : ApiController
{
private Func<string, IUnitOfWork> unitOfWorkFactory;
public FooController( Func<string, IUnitOfWork> unitOfWorkFactory )
{
this.unitOfWorkFactory = unitOfWorkFactory;
}
[Route( "{clientId}" )]
public Foo Get( string clientId )
{
var unitOfWork = unitOfWorkFactory(clientId);
// ...
}
}
All that remains is configuring our dependency injection container to provide us that Func<string, IUnitOfWork>. This could vary significantly between implementation. The following is one possible way to do it in Autofac.
protected override void Load( ContainerBuilder builder )
{
// It is expected `MyDbContext` has a constructor that takes the connection string as a parameter
// This registration may need to be tweaked depending on what other constructors you have.
builder.Register<MyDbContext>().ForType<DbContext>().InstancePerRequest();
// It is expected `UnitOfWork`'s constructor takes a `DbContext` as a parameter
builder.RegisterType<UnitOfWork>().ForType<IUnitOfWork>().InstancePerRequest();
builder.Register<Func<string, Bar>>(
c =>
{
var dbContextFactory = c.Resolve<Func<string, DbContext>>();
var unitOfWorkFactory = c.Resolve<Func<DbContext, IUnitOfWork>>();
return clientId =>
{
// You may have injected another type to help with this
var connectionString = GetConnectionStringForClient(clientId);
return unitOfWorkFactory(dbContextFactory(connectionString));
};
});
}
Autofac is used since comments indicates Autofac is currently being used, though similar results would be possible with other containers.
With that the controller should be able to be instantiated and the appropriate connection string will be used for each request.
Example registration based on linked project:
builder.Register<Func<string, IEmployeeService>>(
c =>
{
var dbContextFactory = c.Resolve<Func<string, IMainContext>>();
var unitOfWorkFactory = c.Resolve<Func<IMainContext, IUnitOfWork>>();
var repositoryFactory = c.Resolve<Func<IMainContext, IEmployeeRepository>>();
var serviceFactory = c.Resolve<Func<IUnitOfWork, IEmployeeService>>();
return clientId =>
{
// You may have injected another type to help with this
var connectionString = GetConnectionStringForClient(clientId);
IMainContext dbContext = dbContextFactory(connectionString);
IUnitOfWork unitOfWork = unitOfWorkFactory(dbContext);
IEmployeeRepository employeeRepository = repositoryFactory(dbContext);
unitOfWork.employeeRepositoty = employeeRepository;
return serviceFactory(unitOfWork);
};
});
If you find the registration grows too cumbersome because of needing to do a little wiring manually, you probably need to look at updating (or creating a new) container after you have determined the client so that you can rely more on the container.
You can change the connectionstring per DbContext instance
Example:
public class AwesomeContext : DbContext
{
public AwesomeContext (string connectionString)
: base(connectionString)
{
}
public DbSet<AwesomePeople> AwesomePeoples { get; set; }
}
And then use your DbContext like this:
using(AwesomeContext context = new AwesomeContext("newConnectionString"))
{
return context.AwesomePeoples.ToList();
}
Depending on how many ConnectionStrings there are you can make a DB table for the client / constring mapping or save it in the solution (array for example).
If you can't/don't want to change the constructor you can do it later as well
Add this to your DbContext override:
public void SetConnectionString(string connectionString)
{
this.Database.Connection.ConnectionString = connectionString;
}
And call the method before you do any DB operations:
using(AwesomeContext context = new AwesomeContext())
{
context.SetConnectionString(ConfigurationManager.ConnectionStrings["newConnectionString"].ConnectionString)
return context.AwesomePeoples.ToList();
}
I have some custom fields on my User object that I want to access with APEX code in my VisualForce trigger. When I access it from a Formula field I get to use a nifty $User reference like this:
$User.my_prop__c
From APEX I have to query the User object by UserId like this:
[select my_prop__c from User where id = :UserInfo.getUserId()].my_prop__c;
Is there something baked into APEX already that would let me get at the user properties without the SOQL query? If not, does anyone know of a utility class for lazy loading and caching user properties so the overhead is minimal.
I would use something similar to the following code sample. It uses a singleton pattern to statically store the information in memory for the duration of your transaction. It's similar to the lazy loading that twamley proposed but I feel this is a much simpler approach.
Usage 1: UserUtil.CurrentUser.Email;
Usage 2: User someUser = UserUtil.getUser(someUserId);
This will allow you to access the same information on the current user or other users in the system. Notice the queryUsers method just returns a query result. This makes it easy to add and remove fields from your query as it is isolated in its own method keeping things simple.
Note: that this code pulls in all users when used. Most orgs do not have multiple hundreds of users so heap size shouldn't be a concern. But if it is you can just modify the queryUsers() method to only return active users or filter down based on other criteria.
public class UserUtil {
//Protected Members
private static final UserUtil instance = new UserUtil();
private Map<Id, User> mapUsers;
//Properties
public static User CurrentUser {
get { return getUser(UserInfo.getUserId()); }
}
//Constructor
private UserUtil() {
mapUsers = new Map<Id, User>(queryUsers());
}
//Public Methods
public static User getUser(Id userId) {
if (instance.mapUsers.containsKey(userId)) {
return instance.mapUsers.get(userId);
}
else {
throw new InvalidUserIdException('Unable to locate user id: ' + userId);
}
}
//Private Methods
private List<User> queryUsers() {
return [SELECT
Id
, Name
, UserName
, Email
, Alias
FROM
User];
}
//Internal Classes
public class InvalidUserIdException extends Exception {}
}
I wrote my own utility class. I'm still interested in better techniques though.
This utility class lazy loads when the first property is accessed. Update_Closed_Won_Opportunities__c and Set_Opportunities_to_Closed_Won__c are my custom fields on the User object (visible only to System Administrators so people can't upgrade their permissions).
public with sharing class MyUserInfo {
private Id userId;
private User myUser; // Hold onto the user object once we've loaded it
// Default constructor uses the active user id
public MyUserInfo() {
userId = UserInfo.getUserId();
}
// Secondary constructor accepts a user id as a parameter
public MyUserInfo(Id someOtherUserId) {
userId = someOtherUserId;
}
// Only called one time when we first need it so grab all of the custom fields now
private void LazyLoadUser() {
System.AssertNotEquals(null, userId);
myUser = [
SELECT Update_Closed_Won_Opportunities__c, Set_Opportunities_To_Closed_Won__c
FROM User
WHERE id = :userId
];
System.AssertNotEquals(null, myUser, 'Unable to load user with id ' + userId); // could return defaults instead
}
// Getters (be sure to include each field in the SOQL of LazyLoadUser)
public boolean UpdateClosedWonOpportunities { get {
if (myUser == null) LazyLoadUser();
return myUser.Update_Closed_Won_Opportunities__c;
} }
public boolean SetOpportunitiesToClosedWon { get {
if (myUser == null) LazyLoadUser();
return myUser.Set_Opportunities_To_Closed_Won__c;
} }
}
Here is my trigger utilizing that class. The first line myUserInfo = new MyUserInfo(); doesn't run any SOQL. That won't happen until the first custom get property is used. Subsequent calls don't need SOQL.
trigger LockClosedOpportunity on Opportunity (before update) {
MyUserInfo myUserInfo = new MyUserInfo();
for (Opportunity o : trigger.old)
{
if (!myUserInfo.UpdateClosedWonOpportunities && o.StageName == 'Closed Won')
trigger.newMap.get(o.Id).addError('You do not have permission to change an Opportunity after it has been set to Closed Won.');
}
for (Opportunity o : trigger.new)
{
if ( !myUserInfo.SetOpportunitiesToClosedWon && o.StageName == 'Closed Won' && trigger.oldMap.get(o.Id).StageName != 'Closed Won' )
o.addError('You do not have permission to set an Opportunity to Closed Won.');
}
}
It reads similar to $User in formulas and I don't have to worry about tacking on multiple SOQL calls when one (or zero) suffices.
I have an issue with my model in EF model first. I have a web app, with security layer and entity layer and mvc3 layer. So in each layer I put a instance of my context db (one for each class in the main declaration section). the problem is when y find any user of my db for validate the login process, simple search, not make any changes in entity, is like this:
var usr = db.Usuarios.First(user => user.UserName.Equals(userName));
If I change the password for example in other layer (in my controller user), and the logout an login again, the linq search (see code up) always return the old password. This is for the context db dont dispose and dont go to database for the data, simple load the user for the model.
So, how I can force the model to refresh the data from the database?
I try, put lazy load in false but not work.
db.Configuration.LazyLoadingEnabled = false;
partial work if I decalre a instance of my context in the metod to validate passwork and dispose this instance, but I think it is the best practice.
like that:
public static bool ValidateUser(string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
var dbtmp = new ConorContainer();
var usr = dbtmp.Usuarios.First(user => user.UserName.Equals(userName));
if (usr != null)
{
var passDescrypt = Decript(usr.Password);
dbtmp.Dispose();
return passDescrypt.Equals(password);
}
return false;
}
return false;
}
You have to decide the lifetime of the context in each case. It is preferred to use short lived contexts in web applications specially in static methods.
Your method can be improved by employing the using block that will call the Dispose method when going out of scope.
public static bool ValidateUser(string userName, string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
using(var dbtmp = new ConorContainer())
{
var usr = dbtmp.Usuarios.First(user => user.UserName.Equals(userName));
if (usr != null)
{
var passDescrypt = Decript(usr.Password);
return passDescrypt.Equals(password);
}
}
return false;
}
return false;
}
I use this style when I get data from database
public class User
{
public static List<User> GetUsers()
{
List<User> users = new Users();
using ( var context = new DataContext())
{
users = context.Users.ToList();
}
return users;
}
}
After I get data from database then I want to Filter user likes this
List<User> userResultList = User.GetUsers();
userResultList.Where(u => u.IsActive == true);
But cannot filter and I get this error
The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.
My Question :
Is possible way to filter result object after DataContext disposed?
Thank you very much for every support.
Update !!!
I think it possible if I create new DbContext in method scope that I want to retrieve data
Is it good enough approach?
Example
public class User
{
public static List<User> GetUsers()
{
List<User> users = new Users();
var context = new DataContext())
users = context.Users.ToList();
return users;
}
}
I wonder if you clone the list will get around the error?
User[] L = new User[users.Count];
users.CopyTo(L);
Speculation:
What you had looks like it should work on the surface.
I wonder if the thing that is returned by ToList() is something that inherits from list, and retains some hidden context awareness inside.
I can't test this idea right now. Something to think about for later though.
ref:
http://www.lunatech-research.com/playframework-file-upload-blob
I'm uneasy about one point in this example
#{list items:models.User.findAll(), as:'user'}
<img src="#{userPhoto(user.id)}">
#{/list}
At this point I'm already holding the user object (including the image blob). Yet the userPhoto() method makes another dip into the backend to get the Image user.photo
public static void userPhoto(long id) {
final User user = User.findById(id);
notFoundIfNull(user);
response.setContentTypeIfNotSet(user.photo.type());
renderBinary(user.photo.get());
}
Any way to avoid this unnecessary findById call?
You're not actually holding the user object any more though, because the userPhoto action is invoked in a separate request that's sent when the browser tries to load the image from the URL generated by #{userPhoto(user.id)}.
Of course, you could use the cache to store data from each user's photo Blob, which would reduce the likelihood that you had to go to the database on the image request. It's more trouble than it's worth in this case though since you're just doing a simple primary key lookup for the user object, and that should be relatively inexpensive. Plus Blobs aren't serializable, so you have to pull out each piece of information separately.
Still, if you were to try that it might look something like this:
// The action that renders your list of images
public static void index() {
List<User> users = User.findAll();
for (User user : users) {
cachePhoto(user.photo);
}
render(users);
}
// The action that returns the image data to display
public static void userPhoto(long id) {
InputStream photoStream;
String path = Cache.get("image_path_user_" + id);
String type = Cache.get("image_type_user_" + id);
// Was the data we needed in the cache?
if (path == null || type == null) {
// No, we'll have to go to the database anyway
User user = User.findById(id);
notFoundIfNull(user);
cachePhoto(user.photo);
photoStream = user.photo.get();
type = user.photo.type();
} else {
// Yes, just generate the stream directly
try {
photoStream = new FileInputStream(new File(path));
} catch (Exception ex) {
throw new UnexpectedException(ex);
}
}
response.setContentTypeIfNotSet(type);
renderBinary(photoStream);
}
// Convenience method for caching the photo information
private static void cachePhoto(Blob photo) {
if (photo == null) {
return;
}
Cache.set("image_path_user_" + user.id,
photo.getFile.getAbsolutePath());
Cache.set("image_type_user_" + user.id,
photo.getType());
}
Then you'd still have to worry about appropriately populating/invalidating the cache in your add, update, and delete actions too. Otherwise your cache would be polluted with stale data.