예제 #1
0
 public void AddBookToOrder(IEntitySession session, Guid orderId, Guid bookId, int quantity)
 {
     var order = session.GetEntity<IBookOrder>(orderId, LockOptions.ForUpdate);
       var book = session.GetEntity<IBook>(bookId);
       var orderLine = session.NewEntity<IBookOrderLine>();
       orderLine.Order = order;
       orderLine.Book = book;
       orderLine.Quantity = quantity;
       orderLine.Price = book.Price;
       order.Lines.Add(orderLine);
       order.Total = order.Lines.Sum(ol => ol.Price * ol.Quantity);
       session.SaveChanges();
 }
예제 #2
0
        public void AddBookToOrder(IEntitySession session, Guid orderId, Guid bookId, byte quantity)
        {
            var order     = session.GetEntity <IBookOrder>(orderId, LockOptions.ForUpdate);
            var book      = session.GetEntity <IBook>(bookId);
            var orderLine = session.NewEntity <IBookOrderLine>();

            orderLine.Order    = order;
            orderLine.Book     = book;
            orderLine.Quantity = quantity;
            orderLine.Price    = book.Price;
            order.Lines.Add(orderLine);
            order.Total = order.Lines.Sum(ol => ol.Price * ol.Quantity);
            session.SaveChanges();
        }
예제 #3
0
 public BookOrderStats GetBookOrderStats(IEntitySession session, Guid orderId)
 {
     try {
     var order = session.GetEntity<IBookOrder>(orderId, LockOptions.SharedRead);
     return new BookOrderStats() {
       OrderId = orderId, LineCount = order.Lines.Count,
       MaxPrice = order.Lines.Max(ol => ol.Price) };
       } finally {
     session.ReleaseLocks();
       }
 }
예제 #4
0
        private IBookOrder GetOpenOrder(IEntitySession session, LockType lockType, bool create = false)
        {
            var currUserId = Context.User.UserId;
            var openOrder  = session.EntitySet <IBookOrder>(lockType)
                             .Where(bo => bo.User.Id == currUserId && bo.Status == OrderStatus.Open).FirstOrDefault();

            if (openOrder == null && create)
            {
                var user = session.GetEntity <IUser>(Context.User.UserId);
                openOrder = session.NewOrder(user);
            }
            return(openOrder);
        }
예제 #5
0
 public BookOrderStats GetBookOrderStats(IEntitySession session, Guid orderId)
 {
     try {
         var order = session.GetEntity <IBookOrder>(orderId, LockOptions.SharedRead);
         return(new BookOrderStats()
         {
             OrderId = orderId, LineCount = order.Lines.Count,
             MaxPrice = order.Lines.Max(ol => ol.Price)
         });
     } finally {
         session.ReleaseLocks();
     }
 }
예제 #6
0
        /// <summary>Retrieves entity by type and primary key value and sets database lock on the underlying record. </summary>
        /// <typeparam name="TEntity">Entity type.</typeparam>
        /// <param name="session">Entity session.</param>
        /// <param name="primaryKey">The value of the primary key.</param>
        /// <param name="lockType">Lock type.</param>
        /// <returns>An entity instance.</returns>
        /// <remarks>For composite primary keys pass an instance of primary key
        /// created using the <see cref="EntitySessionExtensions.CreatePrimaryKey"/> extension method.
        /// </remarks>
        public static TEntity GetEntity <TEntity>(this IEntitySession session, object primaryKey, LockType lockType)
            where TEntity : class
        {
            if (lockType == LockType.None)
            {
                return(session.GetEntity <TEntity>(primaryKey)); //short path, no locks
            }
            Util.CheckParam(primaryKey, nameof(primaryKey));
            session.LogMessage("-- Locking entity {0}/{1}", typeof(TEntity).Name, primaryKey);
            var       entInfo    = session.Context.App.Model.GetEntityInfo(typeof(TEntity), throwIfNotFound: true);
            var       entSession = (EntitySession)session;
            EntityKey pk         = entInfo.CreatePrimaryKeyInstance(primaryKey);
            var       ent        = entSession.SelectByPrimaryKey(entInfo, pk.Values, lockType);

            return((TEntity)ent);
        }
예제 #7
0
        // Can be called explicitly outside of migrations to unencrypt existing values
        public static void UnencryptFactorValues(IEntitySession session)
        {
            var loginConfig = session.Context.App.GetConfig <LoginModuleSettings>();
            var errLog      = session.Context.App.ErrorLog;
            int batchSize   = 200;
            int skip        = 0;

            try {
                var factors = session.EntitySet <ILoginExtraFactor>()
                              .Where(f => f.FactorValue == null).OrderBy(f => f.CreatedOn)
                              .Skip(skip).Take(batchSize).ToList();
                skip += batchSize;
                if (factors.Count == 0)
                {
                    return;
                }
                //preload all EncryptedValue records
                var encrIds  = factors.Select(f => f.Info_Id).ToList();
                var encrRecs = session.EntitySet <IEncryptedData>().Where(ed => encrIds.Contains(ed.Id));
                foreach (var f in factors)
                {
                    if (f.Info_Id == null || f.Info_Id.Value == Guid.Empty)
                    {
                        continue;
                    }
                    var ed = session.GetEntity <IEncryptedData>(f.Info_Id); //should be preloaded
                    if (ed == null)
                    {
                        continue;
                    }
                    f.FactorValue = ed.DecryptString(loginConfig.EncryptionChannelName);
                    f.Info_Id     = null;
                }
                session.SaveChanges();
            } catch (Exception ex) {
                if (errLog != null)
                {
                    errLog.LogError(ex, session.Context);
                }
                if (!SuppressMigrationErrors)
                {
                    throw;
                }
            }
        }
예제 #8
0
파일: LockHelper.cs 프로젝트: radtek/vita
        /// <summary>Retrieves entity by type and primary key value and sets database lock on the underlying record. </summary>
        /// <typeparam name="TEntity">Entity type.</typeparam>
        /// <param name="primaryKey">The value of the primary key.</param>
        /// <param name="lockType">Lock type.</param>
        /// <returns>An entity instance.</returns>
        /// <remarks>For composite primary keys pass an instance of primary key
        /// created using the <see cref="EntitySessionExtensions.CreatePrimaryKey"/> extension method.
        /// </remarks>
        public static TEntity GetEntity <TEntity>(this IEntitySession session, object primaryKey, LockType lockType)
            where TEntity : class
        {
            if (lockType == LockType.None)
            {
                return(session.GetEntity <TEntity>(primaryKey)); //short path, no locks
            }
            Util.CheckParam(primaryKey, nameof(primaryKey));
            session.LogMessage("-- Locking entity {0}/{1}", typeof(TEntity).Name, primaryKey);
            var entInfo = session.Context.App.Model.GetEntityInfo(typeof(TEntity), throwIfNotFound: true);

            /*
             * var pkMembers = entInfo.PrimaryKey.KeyMembers;
             * Util.Check(pkMembers.Count == 1, "Cannot lock entity {0}: composite primary keys not supported.", entInfo.Name);
             * var pkMember = entInfo.PrimaryKey.KeyMembers[0].Member;
             * var prmEnt = Expression.Parameter(typeof(TEntity), "e");
             * var pkRead = Expression.MakeMemberAccess(prmEnt, pkMember.ClrMemberInfo);
             *
             * // PK box - we could use Constant expr to hold PK value directly, but the result is that PK is embedded into translated SQL as literal.
             * // (that's how Linq translation works). We want a query with parameter, so that translated linq command is cached and reused.
             * // So we add extra Convert expression to trick LINQ translator.
             * var pkValueExpr = Expression.Convert(Expression.Constant(primaryKey, typeof(object)), pkMember.DataType);
             * var eq = Expression.Equal(pkRead, pkValueExpr); // Expression.Constant(primaryKey, pkMember.DataType));
             * var filter = Expression.Lambda<Func<TEntity, bool>>(eq, prmEnt);
             */
            var       entSession = (EntitySession)session;
            EntityKey pk         = entInfo.CreatePrimaryKeyInstance(primaryKey);
            var       ent        = entSession.SelectByPrimaryKey(entInfo, pk.Values, lockType);

            return((TEntity)ent);

            /*
             * var query = session.EntitySet<TEntity>(lockType).Where(filter);
             * // We use ToList() on entire query instead of First() because we have already filter on PK value,
             * // and we want to avoid adding any paging (skip/take) clauses to the SQL.
             * // We use FirstOrDefult on entire list, and check that we got something; if not, we throw clear message.
             * var ent = query.ToList().FirstOrDefault();
             * Util.Check(ent != null, "Entity {0} with ID {1} does not exist, cannot set the lock.", entInfo.EntityType.Name,
             *  primaryKey);
             * return ent;
             */
        }
예제 #9
0
        //For now implemented using dynamically built LINQ query; stored proc support to come later
        // TODO: save filter Func in EntityInfo and reuse it
        public static TEntity GetEntity <TEntity>(this IEntitySession session, object primaryKey, LockOptions options)
            where TEntity : class
        {
            if (options == LockOptions.None)
            {
                return(session.GetEntity <TEntity>(primaryKey)); //short path, no locks
            }
            session.LogMessage("-- Locking entity {0}/{1}", typeof(TEntity).Name, primaryKey);
            var entInfo   = session.Context.App.Model.GetEntityInfo(typeof(TEntity), throwIfNotFound: true);
            var pkMembers = entInfo.PrimaryKey.KeyMembers;

            Util.Check(pkMembers.Count == 1, "Cannot lock entity {0}: composite primary keys not supported.", entInfo.Name);
            var pkMember = entInfo.PrimaryKey.KeyMembers[0].Member;
            var prmEnt   = Expression.Parameter(typeof(TEntity), "e");
            var pkRead   = Expression.MakeMemberAccess(prmEnt, pkMember.ClrMemberInfo);
            var eq       = Expression.Equal(pkRead, Expression.Constant(primaryKey, pkMember.DataType));
            var filter   = Expression.Lambda <Func <TEntity, bool> >(eq, prmEnt);
            var query    = session.EntitySet <TEntity>(options).Where(filter);
            // We use ToList() on entire query instead of First() because we have already filter on PK value,
            // and we want to avoid adding any paging (skip/take) clauses to the SQL.
            // We use FirstOrDefult on entire list, and check that we got something; if not, we throw clear message.
            var ent = query.ToList().FirstOrDefault();

            Util.Check(ent != null, "Entity {0} with ID {1} does not exist, cannot set the lock.", entInfo.EntityType.Name,
                       primaryKey);
            return(ent);

            /*
             * //The following is just a sketch
             * if (checkLastModifiedId != null) {
             * Util.Check(entInfo.VersioningMember != null, "Entity {0} has no tracking/versioning column (last modified transaction id), cannot check data version.", entInfo.Name);
             * var lastTransId = EntityHelper.GetProperty<Guid>(ent, entInfo.VersioningMember.MemberName);
             * session.Context.ThrowIf(doNotUse_checkLastModifiedId.Value != lastTransId, ClientFaultCodes.ConcurrentUpdate, entInfo.VersioningMember.MemberName, "Entity {0} was modified by concurrent process.", entInfo.Name);
             * }
             * */
        }
예제 #10
0
        private void RunRandomReadWriteOp(Guid[] docIds, LockType readLock, LockType writeLock, int readWriteCount)
        {
            var            rand    = new Random(Thread.CurrentThread.ManagedThreadId);
            IEntitySession session = null;
            // Use context with its own buffered op log - all entries related to single load/update operation are buffered,
            // and then flushed together at the end of loop body; so they will appear together in the output file
            var ctx = new OperationContext(_app);

            ctx.Log = new BufferedLog(ctx.LogContext);

            for (int i = 0; i < readWriteCount; i++)
            {
                session = ctx.OpenSession();
                var        randomDocId = docIds[rand.Next(docIds.Length)];
                IDoc       iDoc;
                IDocDetail iDet;
                int        randomOp = -1;
                try {
                    Thread.Yield();
                    var randomValueName = "N" + rand.Next(5);
                    randomOp = rand.Next(7);
                    switch (randomOp)
                    {
                    case 0:
                    case 1: //read and check total
                        session.LogMessage("\r\n----------------- Load, check Doc total ---------------------");
                        iDoc = session.GetEntity <IDoc>(randomDocId, readLock);
                        Thread.Yield(); //to let other thread mess it up
                        var valuesSum = iDoc.Details.Sum(v => v.Value);
                        if (valuesSum != iDoc.Total)
                        {
                            session.LogMessage("!!!! Sum error: Doc.Total: {0}, Sum(Value): {1}", iDoc.Total, valuesSum);
                            Interlocked.Increment(ref _sumErrorCount);
                        }
                        Thread.Yield();
                        session.ReleaseLocks();
                        session.LogMessage("\r\n-------------- Completed Load/check total ---------------------");
                        break;

                    case 2:
                    case 3:
                    case 4:
                        //insert/update, we give it an edge over deletes, to have 2 upserts for 1 delete
                        session.LogMessage("\r\n----------------- Update/insert DocDetail ---------------------");
                        iDoc = session.GetEntity <IDoc>(randomDocId, writeLock);
                        Thread.Yield();
                        iDet = iDoc.Details.FirstOrDefault(iv => iv.Name == randomValueName);
                        if (iDet == null)
                        {
                            iDet      = session.NewEntity <IDocDetail>();
                            iDet.Doc  = iDoc;
                            iDet.Name = randomValueName;
                            iDoc.Details.Add(iDet); //to correctly calculate total
                        }
                        iDet.Value = rand.Next(10);
                        iDoc.Total = iDoc.Details.Sum(v => v.Value);
                        Thread.Yield();
                        session.SaveChanges();
                        session.LogMessage("\r\n------Completed Update/insert doc detail ---------------------");

                        var entSession = (Vita.Entities.Runtime.EntitySession)session;
                        if (entSession.CurrentConnection != null)
                        {
                            Debugger.Break(); //check that connection is closed and removed from session
                        }
                        break;

                    case 6: //delete if exists
                        session.LogMessage("\r\n----------------- Delete doc detail ---------------------");
                        //Note: deletes do not throw any errors - if record does not exist (had been just deleted), stored proc do not throw error
                        iDoc = session.GetEntity <IDoc>(randomDocId, writeLock);
                        Thread.Yield(); //allow others mess up
                        iDet = iDoc.Details.FirstOrDefault(iv => iv.Name == randomValueName);
                        if (iDet != null)
                        {
                            session.DeleteEntity(iDet);
                            iDoc.Details.Remove(iDet);
                            iDoc.Total = iDoc.Details.Sum(v => v.Value);
                            session.SaveChanges(); // even if there's no changes, it will release lock
                        }
                        else
                        {
                            session.ReleaseLocks();
                        }
                        session.LogMessage("\r\n----------------- Completed delete doc detail ---------------------");
                        break;
                    }//switch
                } catch (Exception ex) { //most will be UniqueIndexViolation
                    Debug.WriteLine(ex.ToLogString());
                    System.Threading.Interlocked.Increment(ref _updateErrorCount);
                    session.Context.Log.AddEntry(new ErrorLogEntry(ctx.LogContext, ex));
                    var entSession = (Vita.Entities.Runtime.EntitySession)session;
                    if (entSession.CurrentConnection != null)
                    {
                        entSession.CurrentConnection.Close();
                    }
                    //session.Context.Log.Flush();
                    _app.Flush();
                } finally {
                    //_app.Flush();
                }
                //session.Context.Log.Flush();
            } //for i
        }     //method
예제 #11
0
        private void RunRandomReadWriteOp(Guid[] docIds, LockOptions readOptions, LockOptions writeOptions, int readWriteCount)
        {
            var            rand    = new Random(Thread.CurrentThread.ManagedThreadId);
            IEntitySession session = null;

            for (int i = 0; i < readWriteCount; i++)
            {
                var        randomDocId     = docIds[rand.Next(docIds.Length)];
                var        randomValueName = "N" + rand.Next(5);
                IDoc       iDoc;
                IDocDetail iDet;
                int        randomOp = -1;
                try {
                    Thread.Yield();
                    session  = _app.OpenSession();
                    randomOp = rand.Next(5);
                    switch (randomOp)
                    {
                    case 0:
                    case 1: //insert/update, we give it an edge over deletes, to have 2 upserts for 1 delete
                        session.LogMessage("\r\n----------------- Update/insert value ---------------------");
                        iDoc = session.GetEntity <IDoc>(randomDocId, writeOptions);
                        Thread.Yield();
                        iDet = iDoc.Details.FirstOrDefault(iv => iv.Name == randomValueName);
                        if (iDet == null)
                        {
                            iDet      = session.NewEntity <IDocDetail>();
                            iDet.Doc  = iDoc;
                            iDet.Name = randomValueName;
                            iDoc.Details.Add(iDet); //to correctly calculate total
                        }
                        iDet.Value = rand.Next(10);
                        iDoc.Total = iDoc.Details.Sum(v => v.Value);
                        Thread.Yield();
                        session.SaveChanges();

                        var entSession = (Vita.Entities.Runtime.EntitySession)session;
                        if (entSession.CurrentConnection != null)
                        {
                            Debugger.Break(); //check that connection is closed and removed from session
                        }
                        break;

                    case 2: //delete if exists
                        session.LogMessage("\r\n----------------- Delete value ---------------------");
                        //Note: deletes do not throw any errors - if record does not exist (had been just deleted), stored proc do not throw error
                        iDoc = session.GetEntity <IDoc>(randomDocId, writeOptions);
                        Thread.Yield(); //allow others mess up
                        iDet = iDoc.Details.FirstOrDefault(iv => iv.Name == randomValueName);
                        if (iDet != null)
                        {
                            session.DeleteEntity(iDet);
                            iDoc.Details.Remove(iDet);
                            iDoc.Total = iDoc.Details.Sum(v => v.Value);
                        }
                        Thread.Yield();
                        session.SaveChanges(); // even if there's no changes, it will release lock
                        break;

                    case 3:
                    case 4: //read and check total
                        session.LogMessage("\r\n----------------- Loading doc ---------------------");
                        iDoc = session.GetEntity <IDoc>(randomDocId, readOptions);
                        Thread.Yield(); //to let other thread mess it up
                        var valuesSum = iDoc.Details.Sum(v => v.Value);
                        if (valuesSum != iDoc.Total)
                        {
                            session.LogMessage("!!!! Sum error: Doc.Total: {0}, Sum(Value): {1}", iDoc.Total, valuesSum);
                            Interlocked.Increment(ref _sumErrorCount);
                        }
                        Thread.Yield();
                        session.ReleaseLocks();
                        break;
                    }//switch
                    WriteLog(session);
                } catch (Exception ex) { //most will be UniqueIndexViolation
                    Debug.WriteLine(ex.ToLogString());
                    System.Threading.Interlocked.Increment(ref _updateErrorCount);
                    if (session != null)
                    {
                        WriteLog(session);
                        var log = session.Context.LocalLog.GetAllAsText();
                        Debug.WriteLine(log);
                        var entSession = (Vita.Entities.Runtime.EntitySession)session;
                        if (entSession.CurrentConnection != null)
                        {
                            entSession.CurrentConnection.Close();
                        }
                    }
                }
            } //for i
        }     //method
예제 #12
0
 private IBookOrder GetOpenOrder(IEntitySession session, LockOptions lockOptions, bool create = false)
 {
     var currUserId = Context.User.UserId;
       var openOrder = session.EntitySet<IBookOrder>(lockOptions)
     .Where(bo => bo.User.Id == currUserId && bo.Status == OrderStatus.Open).FirstOrDefault();
       if (openOrder == null && create) {
     var user = session.GetEntity<IUser>(Context.User.UserId);
     openOrder = session.NewOrder(user);
       }
       return openOrder;
 }
예제 #13
0
 public ILogin GetLogin(IEntitySession session, Guid loginId)
 {
     return session.GetEntity<ILogin>(loginId);
 }
예제 #14
0
 public ILogin GetLogin(IEntitySession session, Guid loginId)
 {
     return(session.GetEntity <ILogin>(loginId));
 }
예제 #15
0
        // If necessary, can be called explicitly outside of migrations to unencrypt existing values
        public static void UnencryptTokens(IEntitySession session)
        {
            var config = session.Context.App.GetConfig <OAuthClientSettings>();
            var errLog = session.Context.App.ErrorLog;
            // Unencrypt client secret in remote server accounts - should be a smaill table
            var accts = session.EntitySet <IOAuthRemoteServerAccount>().Where(a => a.ClientSecret_Id != null).Take(100).ToList();

            foreach (var acct in accts)
            {
                var ed = session.GetEntity <IEncryptedData>(acct.ClientSecret_Id);
                if (ed == null)
                {
                    continue;
                }
                acct.ClientSecret    = ed.DecryptString(config.EncryptionChannel);
                acct.ClientSecret_Id = null;
            }
            session.SaveChanges();

            // Tokens - might be big table, process in batches
            int batchSize = 200;
            int skip      = 0;

            try {
                var tokenRecs = session.EntitySet <IOAuthAccessToken>()
                                .Where(tr => tr.AccessToken == null).OrderBy(tr => tr.RetrievedOn)
                                .Skip(skip).Take(batchSize).ToList();
                skip += batchSize;
                if (tokenRecs.Count == 0)
                {
                    return;
                }
                //preload all EncryptedValue records
                var encrIds1 = tokenRecs.Where(tr => tr.AccessToken_Id != null).Select(tr => tr.AccessToken_Id.Value).ToList();
                var encrIds2 = tokenRecs.Where(tr => tr.RefreshToken_Id != null).Select(tr => tr.RefreshToken_Id.Value).ToList();
                encrIds1.AddRange(encrIds2);
                var encrRecs = session.EntitySet <IEncryptedData>().Where(ed => encrIds1.Contains(ed.Id));
                foreach (var tr in tokenRecs)
                {
                    //Access token
                    var eId = tr.AccessToken_Id;
                    if (eId != null && eId.Value != Guid.Empty)
                    {
                        var ed = session.GetEntity <IEncryptedData>(eId.Value); //should be preloaded
                        if (ed != null)
                        {
                            tr.AccessToken    = ed.DecryptString(config.EncryptionChannel);
                            tr.AccessToken_Id = null;
                        }
                    }
                    // Refresh token
                    eId = tr.AccessToken_Id;
                    if (eId != null && eId.Value != Guid.Empty)
                    {
                        var ed = session.GetEntity <IEncryptedData>(eId.Value); //should be preloaded
                        if (ed != null)
                        {
                            tr.RefreshToken    = ed.DecryptString(config.EncryptionChannel);
                            tr.RefreshToken_Id = null;
                        }
                    }
                } //foreach tr
                session.SaveChanges();
            } catch (Exception ex) {
                if (errLog != null)
                {
                    errLog.LogError(ex, session.Context);
                }
                if (!SuppressMigrationErrors)
                {
                    throw;
                }
            }
        } //method