I have the the following Repository with cache
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
}
public interface IUserRepository
{
User GetUser(int userId);
}
public class CacheObject
{
public int UserId { get; set; }
public User User { get; set; }
public DateTime CreationDate { get; set; }
}
public class CachedUserRepository : IUserRepository
{
private IUserRepository _userRepository;
private List<CacheObject> _cache = new List<CacheObject>();
private int _cacheDuration = 60;
public CachedUserRepository(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public User GetUser(int userId)
{
bool addToCache = false;
CacheObject valueFromCache = _cache.SingleOrDefault(u => u.UserId == userId);
// user was found
if (valueFromCache != null)
{
// if cache is outdated then remove value from it
if (valueFromCache.CreationDate.AddSeconds(_cacheDuration) < DateTime.Now)
{
_cache.Remove(valueFromCache);
addToCache = true;
}
else {
// update cache date
valueFromCache.CreationDate = DateTime.Now;
return valueFromCache.User;
}
}
// user is absent in cache
else {
addToCache = true;
}
if (addToCache)
{
User result = _userRepository.GetUser(userId);
_cache.Add(new CacheObject() { User = result, UserId = userId, CreationDate = DateTime.Now });
return result;
}
return null;
}
}
I would like to run method GetUser() in different threads so i need to make this method thread safe.
How can i make it ?
I don't see any elegant solution , only lock(someObject) to the whole method body. But as result i will not achieve any performance gain
We usually do this with a ReaderWriterLockSlim like this:
public class CachedUserRepository : IUserRepository
{
private readonly ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
private IUserRepository _userRepository;
private List<CacheObject> _cache = new List<CacheObject>();
private int _cacheDuration = 60;
public CachedUserRepository(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public User GetUser(int userId)
{
bool addToCache = false;
// Enter an upgradeable read lock because we might have to use a write lock if having to update the cache
// Multiple threads can read the cache at the same time
_cacheLock.EnterUpgradeableReadLock();
try
{
CacheObject valueFromCache = _cache.SingleOrDefault(u => u.UserId == userId);
// user was found
if (valueFromCache != null)
{
// if cache is outdated then remove value from it
if (valueFromCache.CreationDate.AddSeconds(_cacheDuration) < DateTime.Now)
{
// Upgrade to a write lock, as an item has to be removed from the cache.
// We will only enter the write lock if nobody holds either a read or write lock
_cacheLock.EnterWriteLock();
try
{
_cache.Remove(valueFromCache);
}
finally
{
_cacheLock.ExitWriteLock();
}
addToCache = true;
}
else
{
// update cache date
valueFromCache.CreationDate = DateTime.Now;
return valueFromCache.User;
}
}
// user is absent in cache
else
{
addToCache = true;
}
if (addToCache)
{
User result = _userRepository.GetUser(userId);
// Upgrade to a write lock, as an item will (probably) be added to the cache.
// We will only enter the write lock if nobody holds either a read or write lock
_cacheLock.EnterWriteLock();
try
{
if (_cache.Any(u => u.UserId != userId))
{
_cache.Add(new CacheObject() {User = result, UserId = userId, CreationDate = DateTime.Now});
}
}
finally
{
_cacheLock.ExitWriteLock();
}
return result;
}
}
finally
{
_cacheLock.ExitUpgradeableReadLock();
}
return null;
}
}
With this, multiple threads will be able to read the cache simultaneously, but if it has to be written, it will get locked.
Disclaimer: I haven't run the code to check it ;)
Related
I am using session to store my user information and I want to know how to give access for each user to his private folder of file?
I want that each user will have access to his unique one folder and the manager will have access for whole folder. This can be done by session?
This is User.cs:
[Table("Users")]
public class Users
{
[Key]
public string UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[NotMapped]
public string ConfirmPassword { get; set; }
public string Role { get; set; }
public Guid? ResetPasswordCode { get; set; }
public bool IsEmailVerified { get; set; }
public Guid ActivationCode { get; set; }
}
DbContext Connect to Db:
public class DikanDbContext : DbContext {
public DikanDbContext() : base ("name=DikanNetDB")
{ }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
public DbSet<Users> Users { get; set; }
}
Login Controller:
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(UserLogin loginuser)
{
ViewBag.Status = false;
if (ModelState.IsValid)
{
using (DikanDbContext ctx = new DikanDbContext())
{
var account = ctx.Users.Where(e => e.UserId == loginuser.UserId).FirstOrDefault();
if (account != null)
{
if (string.Compare(Crypto.Hash(loginuser.Password), account.Password) == 0)
{
int timeout = loginuser.RememberMe ? 525600 : 120; // one year or 1.5 hour
switch (account.Role) // checks the role of the account to direct to their controller
{
case "Student":
if (account.IsEmailVerified) // checks if the student has verify with email
{
HttpContext.Session.Add("Student", account);
Session.Timeout = timeout;
var student = ctx.Students.Where(s => s.StudentId == account.UserId).FirstOrDefault();
if (student != null) // if the account found in student table
return RedirectToAction("Index", "Student");
else // not found in student table-> go to fill basic info
{
ViewBag.Status = true;
return RedirectToAction("UpdateStudent","Student");
}
}
return View();
case "Admin":
HttpContext.Session.Add("admin",account);
Session.Timeout = timeout;
return RedirectToAction("Index","Admin");
default: break;
}
}
}
}
}
return View(loginuser);
}
}
Update - Each user can upload their own file and the files will be saves in file system under UsersFiles-> UserId
If I Write in url the route to other UserId I have the access to their files so, I want to block this operation How I can do that
Files Class:
public static string SaveFileInServer(HttpPostedFileBase pFile, string pFileName, string pId,string pOldFile)
{
int fileSize = pFile.ContentLength;
var fileExt = Path.GetExtension(pFile.FileName);
var serverpathsave = Path.Combine(HttpContext.Current.Server.MapPath("~/UsersFiles/") + pId);
if (!Directory.Exists(serverpathsave))
Directory.CreateDirectory(serverpathsave);
if (!string.IsNullOrEmpty(pOldFile))
File.Delete(Path.Combine(serverpathsave ,pOldFile));
pFile.SaveAs(Path.Combine(serverpathsave, pFileName + fileExt)); // save file to server
return pFileName+fileExt;
}
/* The function get a file name and id of student
* and delete the file from the server database
*/
public static bool Delete(string pFileName, string pId)
{
if (string.IsNullOrEmpty(pFileName) || string.IsNullOrEmpty(pId))
return false;
var serverpath = Path.Combine(HttpContext.Current.Server.MapPath("~/UsersFiles/") + pId);
if (!Directory.Exists(serverpath)) return false;
File.Delete(Path.Combine(serverpath, pFileName));
return true;
}
Picture Of File System each user has folder with his UserId:
enter image description here
Example of adding file to server
public ActionResult UpdateStudent(Student UpdateStudent)
{
if(UpdateStudent.FileId != null)
UpdateStudent.PathId = Files.SaveFileInServer(UpdateStudent.FileId, "Id", UpdateStudent.StudentId,(dbStudent == null) ? null:dbStudent.PathId);
if (dbStudent == null) // after first login fill more info
ctx.Students.Add(UpdateStudent); // add student to data base
else
ctx.Entry(dbStudent).CurrentValues.SetValues(UpdateStudent);// update student
ctx.SaveChanges();
if (tempuser != null && tempuser.IsEmailVerified == false) // if the student change the email disconnect from system
return RedirectToAction("Disconnect", "Login");
return RedirectToAction("Index");
}
}
this issue arose for me when I had events which call multiple event handlers, but pass each one the same entity. Now, because entities are bound to one context in EF, passing the context along to the event handlers violated the one context per unit of work principle.
So the best I could come up with is a kind of factory and custom proxy (in this example only for Player, but I have more EF entities like Human, Participation, etc):
public class PlayerProxy : Player {
protected SnowbiteContext Context;
protected Player RealPlayer;
private long? _playerId;
private string _name;
public static Func<SnowbiteContext, PlayerProxy> NewProxyFactory(string name) =>
(ctx) => new PlayerProxy(ctx) {Name = name};
public static Func<SnowbiteContext, PlayerProxy> NewProxyFactory(long playerId) =>
(ctx) => new PlayerProxy(ctx) {PlayerId = playerId};
public PlayerProxy(SnowbiteContext ctx) {
Context = ctx ?? throw new ArgumentException();
}
public void Populate() {
if (RealPlayer != null) return;
if (_playerId != null) RealPlayer = Context.Players.Single(p => p.PlayerId == _playerId);
if (_name != null)
RealPlayer =
Context.Players.SingleOrDefault(p =>
p.Name == _name) ?? /* Contact external json API, complicated and expensive */;
}
public override long PlayerId {
get {
if (_playerId == null) Populate();
Debug.Assert(_playerId != null, nameof(_playerId) + " != null");
return _playerId.Value;
}
set {
Populate();
RealPlayer.PlayerId = value;
_playerId = null;
}
}
public override string Name {
get {
if (_name == null) Populate();
return _name;
}
set {
Populate();
RealPlayer.Name = value;
_name = null;
}
}
public override Human Human {
get {
Populate();
return RealPlayer.Human;
}
set {
Populate();
RealPlayer.Human = value;
}
}
public override long HumanId {
get {
Populate();
return RealPlayer.HumanId;
}
set {
Populate();
RealPlayer.HumanId = value;
}
}
public override ICollection<Participation> Participations {
get {
Populate();
return RealPlayer.Participations;
}
set {
Populate();
RealPlayer.Participations = value;
}
}
// Etc... for EaGuid, Tag, Rank.
}
Then the events would go like:
private void RCON_event_OnChat(IReadOnlyList<string> words) {
var name = words[1];
if (name == "Server") return;
EvChat?.Invoke(PlayerProxy.NewProxyFactory(name), words[2]);
}
And the event handlers (Player N:1 Human):
void Handler(Func<SnowbiteContext, PlayerProxy> playerProxyFactory, string message) {
using (var ctx = new SnowbiteContext()) {
PlayerProxy playerProxy = playerProxyFactory(ctx);
// use stuff, unit of work being done here, etc.
// but how about relating my proxy to other entities? Will EF handle my derived type correctly?
Human humanFromER = ctx.Humans.Single(/* ... */);
humanFromER.Players.Add(playerProxy);
playerProxy.Human = humanFromER;
// how about 1:N with ICollection<Participation>? Will EF recognize it properly?
playerProxy.Participations.Add(/* some new participation */);
}
}
For reference, here's my Player Entity:
public class Player {
public Player() {
Participations = new List<Participation>();
}
/// <summary>
/// BalzeId / PersonaId
/// </summary>
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public virtual long PlayerId { get; set; }
[Required]
public virtual long HumanId { get; set; }
public virtual Human Human { get; set; }
/// <summary>
/// Not a key. Mostly provided for convenicencet.
/// </summary>
[StringLength(35)]
public virtual string EaGuid { get; set; }
[Index]
[StringLength(60)]
public virtual string Name { get; set; }
[StringLength(4)]
public virtual string Tag { get; set; }
public virtual int Rank { get; set; }
public virtual ICollection<Participation> Participations { get; set; }
}
Or maybe just passing a string name to the event handler and having a simple function to get a player from the DB would have been simpler :D.
Update
Based on Gert Arnold's comment, I came up with this, getting rid of the proxy entirely:
private void RCON_event_OnChat(IReadOnlyList<string> words) {
var name = words[1];
if (name == "Server") return;
if (EvChat != null) {
// additionally, will be easy to convert to async
foreach (var handler in EvChat.GetInvocationList().Cast<Action<SnowbiteContext, Player, string>>()) {
using (var ctx = new SnowbiteContext()) {
var player = ctx.Players.SingleOrDefault(p => p.Name == name) ?? /* expensive resolve */;
handler(ctx, player, words[2]);
}
}
}
}
In my application, I would like to check whether or not a particular user is logged in or not so I can display a status saying that that user is either "online" or "offline" to another user. This question is not about authenticating a user, only about getting the authentication status of a user.
How would I go about achieving this?
I think an option is to use some real-time solutions. SignalR for example.
When a user logs in , you connect it to the hub. OnConnected() action save its state.Then OnDisconnected() remove from "OnlineRepository".
Update with example
Here is how I did this in a asp.Net Mvc 5 app.
A Model class that holds a single user like:
public class SingleConnection
{
public SingleConnection()
{
ConnectionId = new List();
}
public string Id { get; set; }
public List ConnectionId { get; set; }
}
A connection mapping class that helps in adding/removeing and getting a user from list
public class ConnectionMapping
{
private readonly List _connections = new List();
public int Count
{
get
{
return _connections.Count;
}
}
public void Add(string key, string connectionId)
{
lock (_connections)
{
var sn = _connections.Where(x => x.Id == key).FirstOrDefault();
if (sn != null) // there is a connection with this key
{
_connections.Find(x => x.Id == key).ConnectionId.Add(connectionId);
}
else
{
_connections.Add(new SingleConnection { Id = key, ConnectionId = new List { connectionId } });
}
}
}
public List GetConnections(string id)
{
var con = _connections.Find(x => x.Id == id);
return con != null ? con.ConnectionId : new List();
}
public List AllConnectionIds()
{
List results = new List();
var allItems = _connections.Where(x => x.ConnectionId.Count > 0).ToList();
foreach (var item in allItems)
{
results.AddRange(item.ConnectionId);
}
return results;
}
public List AllKeys()
{
return _connections.Select(x => x.Id).ToList();
}
public void Remove(string key, string connectionId)
{
lock (_connections)
{
var item = _connections.Find(x => x.Id == key);
if (_connections.Find(x => x.Id == key) != null)
{
_connections.Find(x => x.Id == key).ConnectionId.Remove(connectionId);
if (_connections.Find(x => x.Id == key).ConnectionId.Count == 0)
{
_connections.Remove(item);
}
}
}
}
}
In my Hub Class
private void IsActive(string connection, bool connected)
{
Clients.All.clientconnected(connection, connected);
}
public override Task OnConnected()
{
string name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
IsActive(name, true);
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
string name = Context.User.Identity.Name;
_connections.Remove(name, Context.ConnectionId);
IsActive(name, false);
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
string name = Context.User.Identity.Name;
if (!_connections.GetConnections(name).Contains(Context.ConnectionId))
{
_connections.Add(name, Context.ConnectionId);
}
IsActive(name, false);
return base.OnReconnected();
}
In _Layout.cshtml
// reference scripts
// add a callback or the OnConnected() Will not fire
chat.client.clientconnected = function (id,active){
/*
this will be called everytime a user connect or disconnect to the hub
*/
}
$.connection.hub.start();
Now with this I get in realtime all users that are online.
Note: This is an InMemory solution. Other solutions are here
Hope this helps...
I would create my own system to define what are your "active" users:
You can keep the track of your users with a dictionary stored in the System.Web.Caching.Cache class.
In your base controller (because it would be instantiate for any
client request) insert the current user to your cached dictionary
by calling the KeepTrackActiveUsers method.
Add the SetInactiveUser method to your logout method.
{
private Dictionary<int,DateTime> ActiveUsers
{
get
{
if(Cache["ActiveUsers"] == null)
Cache["ActiveUsers"] = new Dictionary<int,DateTime>();
return Cache["ActiveUsers"];
}
set { Cache["ActiveUsers"] = value; }
}
private void KeepTrackActiveUsers()
{
ActiveUsers[CurrentUserId] = DateTime.Now;
}
private const int expirationTime = 600;
private IEnumerable<int> GetActiveUsers()
{
DateTime now = DateTime.Now;
ActiveUsers = ActiveUsers
.Where(x => now.Subtract(x.Value).TotalSeconds < expirationTime)
.ToDictionary(x => x.Key, x => x.Value);
return ActiveUsers.Keys;
}
private void SetInactiveUser()
{
ActiveUsers.Remove(CurrentUserId);
}
//to be defined
private int CurrentUserId
{
get { return default(int); }
}
I have tried a lot but all in vain.
I have written a LINQ code but not able to save changes in database.
It is giving no error neither it is updating record.
class Program
{
[Table(Name = "mainframe_replication")]
public class mainframe_replication
{
private string _REPL_GUID;
[Column(IsPrimaryKey = true, Storage = "_REPL_GUID")]
public string REPL_GUID
{
get { return this._REPL_GUID; }
set { this._REPL_GUID = value; }
}
private string _REPL_TYPE;
[Column(Storage = "_REPL_TYPE")]
public string REPL_TYPE
{
get { return this._REPL_TYPE; }
set { this._REPL_TYPE = value; }
}
private string _RPT_ID;
[Column(Storage = "_RPT_ID")]
public string RPT_ID
{
get { return this._RPT_ID; }
set { this._RPT_ID = value; }
}
private string _RPT_VERS;
[Column(Storage = "_RPT_VERS")]
public string RPT_VERS
{
get { return this._RPT_VERS; }
set { this._RPT_VERS = value; }
}
private string _RPT_BYTES;
[Column(Storage = "_RPT_BYTES")]
public string RPT_BYTES
{
get { return this._RPT_BYTES; }
set { this._RPT_BYTES = value; }
}
private string _REPL_DTM;
[Column(Storage = "_REPL_DTM")]
public string REPL_DTM
{
get { return this._REPL_DTM; }
set { this._REPL_DTM = value; }
}
private string _NOTIF_ID;
[Column(Storage = "_NOTIF_ID")]
public string NOTIF_ID
{
get { return this._NOTIF_ID; }
set { this._NOTIF_ID = value; }
}
}
public class MyPoco
{
public string ReportId { get; set; }
public string Reportversion { get; set; }
public string ReportBytes { get; set; }
public string ReportDate { get; set; }
public string NotifId { get; set; }
public string RecipAdd { get; set; }
}
public static string loglocation;
static void Main(string[] args)
{
try
{
using (DataClasses1DataContext db = new DataClasses1DataContext())
{
Table<NOTIF_RECIP> NOTIF_RECIP_alias = db.GetTable<NOTIF_RECIP>();
Table<NOTIF_SCHED> NOTIF_SCHED_alias = db.GetTable<NOTIF_SCHED>();
Table<mainframe_replication> mainframe_replication_alias = db.GetTable<mainframe_replication>();
var ids = NOTIF_SCHED_alias.Select(x => x.NOTIF_RPT_ID).ToArray();
foreach (string notif_sched_data in ids)
{
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias, mfr => mfr.RPT_ID, nr => nr.NOTIF_RECIP_ID, (mfr, nr)
=> new MyPoco { ReportId = mfr.RPT_ID, Reportversion = mfr.RPT_VERS, ReportBytes = mfr.RPT_BYTES.ToString(), ReportDate = mfr.REPL_DTM.ToString(), NotifId = mfr.NOTIF_ID, RecipAdd = nr.NOTIF_RECIP_ADDR });
foreach (var repljoinmf_data in repljoinmf)
{
repljoinmf_data.NotifId = "abc";
//DO STUFF
// repljoinmf_data.NotifId = "Changedxyz";
}
db.SubmitChanges();
}
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
It is not giving any error while submitting changes.
What I need to change?
Any suggestion will be helpful.
If you want to save your changes back to the original data source, you need to be working with the actual entities instead of projections of those entities. Since you are joining two tables, one option is to put those instances into an anonymous type and update them:
foreach (string notif_sched_data in ids)
{
var repljoinmf = mainframe_replication_alias
.Join(NOTIF_RECIP_alias,
mfr => mfr.RPT_ID,
nr => nr.NOTIF_RECIP_ID,
(mfr, nr) => new {mfr, nr});
foreach (var repljoinmf_data in repljoinmf)
{
//DO STUFF
repljoinmf_data.mfr.NotifId = "Changedxyz";
}
db.SubmitChanges();
In your previous question you were told that anonymous types cannot be uptated, but in this case you're modifying instances that are referenced by the anonymous type. So you're not updating the anonymous type itself, just the objects that the anonymous type references.
You are modifying the property of your MyPoco object. This is just a representation of your table. That's why the database is not updated.
You can send your MyPoco to your client. It will perform some changes. Then you can recreate the entity and copy the properties from the Poco object. Then, you need to attach the modified entity to your table and then save the changes.
If you modify directly the entity, there is no need to attach, since it will have kept the links to the database (assuming you do that with the same Databasecontext).
Basically, i have this class:
public class Promotion : Entity<int>
{
public Promotion()
{
Created = DateTime.Now;
this.files = new HashedSet<PromoImage>();
}
public Promotion(int id, string Name, DateTime created, DateTime from, DateTime to, ISet<PromoImage> files)
{
this.Id = id;
this.Created = created;
this.From = from;
this.To = to;
this.files = files;
}
public Promotion(string Name, DateTime created,DateTime from, DateTime to, ISet<PromoImage> files)
{
this.Created = created;
this.From = from;
this.To = to;
this.files = files;
}
public virtual DateTime Created { get; protected set; }
public virtual string Name { get; protected set; }
public virtual DateTime? From { get; protected set; }
public virtual DateTime? To { get; protected set; }
private ISet<PromoImage> files;
public IEnumerable<PromoImage> Files
{
get
{
return files;
}
}
public virtual bool AddPromoImage(PromoImage newPromoImage)
{
if (newPromoImage != null && files.Add(newPromoImage))
{
return true;
}
return false;
}
public virtual bool RemovePromoImage(PromoImage promoImage)
{
if (promoImage != null && files.Remove(promoImage))
{
return true;
}
return false;
}
public virtual bool ChangePhotoPositions(IEnumerable<Row> rows)
{
foreach (var row in rows)
{
List<PromoImage> photos = Files.Where(p => row.PhotoIdAndPosition.Any(q => q.First == p.Id)).ToList();
int totalWidth = 0;
int lastHeight = photos[0].Image.Height;
bool allPhotosHaveSameHeight = true;
foreach (var photo in photos)
{
allPhotosHaveSameHeight = lastHeight == photo.Image.Height;
totalWidth += photo.Image.Width;
lastHeight = photo.Image.Height;
}
if (totalWidth > 734 && !allPhotosHaveSameHeight)
{
return false;
}
else
{
foreach (var photo in photos)
{
var newPosition = row.PhotoIdAndPosition.Single(p => p.First == photo.Id).Second;
photo.Row = row.Level;
photo.Position = newPosition;
}
}
}
return true;
}
public virtual bool SetPromotionDateRange(DateTime from, DateTime to)
{
if (from > DateTime.Now)
{
if (from > to)
{
From = from;
To = to;
return true;
}
else
{
return false;
}
}
else
return false;
}
}
and this class which stores any changes to the Promotion object as persistent events:
public class StagedPromotion:Promotion
{
public StagedPromotion():base()
{
Changes = new HashedSet<Change>();
}
public virtual DateTime? CommitedWhen { get; protected set; }
public virtual ISet<Change> Changes { get; protected set; }
public virtual Promotion WorkingPromotion
{
get
{
Promotion promotion = new Promotion(this.Name, this.Created, this.From.Value, this.To.Value, this.files);
foreach (var change in Changes)
{
change.ExecuteChange(promotion);
}
return promotion;
}
}
public virtual bool ChangePhotoPositions(IEnumerable<Row> rows)
{
var promo = WorkingPromotion;
if (promo.ChangePhotoPositions(rows))
{
Change change = new ChangeRowAndPosition(new HashedSet<Row>(rows.ToList()));
Changes.Add(change);
return true;
}
else
return false;
}
public virtual bool SetPromotionDateRange(DateTime from, DateTime to)
{
var promo = WorkingPromotion;
if (promo.SetPromotionDateRange(from, to))
{
Change change = new ChangePromotionDate(from, to);
Changes.Add(change);
return true;
}
else
return false;
}
public virtual bool AddPromoImage(PromoImage newPromoImage)
{
//todo
}
public virtual bool RemovePromoImage(PromoImage promoImage)
{
//todo
}
public virtual IEnumerable<PromoImage> Files
{
get
{
var files = base.Files;
//replace each PromoImage object with a StagedPromoImage Decorator
//which registers any changes to the PromoImage as persistent events
}
}
}
Is it possible for NHibernate to return through the base.Files the lazily loaded collection and leave the StagedPromotion's Files property intact?
The idea is to create a versioning system, where each i can keep track of commited/uncommited Promotions.
you refer to base.Files as something that nhibernate would manage, but there is no way to map it as it stands. in order for nhibernate to provide a lazy collection the property would have to be virtual anyway so you couldn't have your staged promotions Files property without overriding the base property.