NHibernate в многопользовательской среде
От: Visor2004  
Дата: 10.08.10 20:41
Оценка:
Пишу небольшой утиль для управления складом, в системе работает порядка 100 пользователей, кладовщиков, продавцов и т.д. Есть WCF сервис (InstanceContextMode = PerSession), который предоставляет функционал для типовых складских операций, внутри использует NHibernate. Возник вопрос, как сделать блокировки данных при изменении одной и той же записи на складе разными пользователями одновременно. Например кейс: есть на складе 100 полотенец, два продавца одновременно оформляют продажу один на 40, второй на 60, при оформлении продажи у сервиса вызывается метод, что-то типа такого: Sell(Sell value, params SellEntry [ ] entries); возникает вопрос, что будет происходить в NHibernate в случае такой реализации метода Sell (см. ниже). Конкретно интересует как заставить NHibernate кидать исключение и отменять транзакцию при попытке продать товара больше чем есть?

[OperationContract]
public Sell Sell(Sell value, params SellEntry [ ] entries)
{
     decimal totalCost = 0.0M;

      using ( ISession session = DataEngine.Factory.OpenSession ( ) )
      {
        using ( ITransaction transaction = session.BeginTransaction ( ) )
        {
            value.Saller = GetCurrentUser ( );
            value.Date = DateTime.Now;

            StringBuilder builder = new StringBuilder ( 128 );
            foreach ( var current in entries )
              builder.AppendFormat ( "{0},", current.Id );
            string s = builder.ToString ( );

            string text = string.Format ( "Select * from Warehouse where Warehouse.Id in (0)", s.Remove ( s.Length - 1, 1 ) );
            IEnumerable<WarehouseEntry> whEntries = session.CreateSQLQuery ( text ).List<WarehouseEntry> ( );

            foreach ( var current in entries )
            {
              foreach ( var refreshed in whEntries )
                if ( current.WhEntry.Id == refreshed.Id )
                {
                  if ( refreshed.Quantity < current.Quantity )
                  {
                    string message = string.Format ( "Невозможно продать {0} в количестве {1} штук. Остаток на складе {2} штук",
                      current.Product.Name, current.Quantity, refreshed.Quantity );
                    throw new FaultException<ArgumentException> ( new ArgumentException ( message ) );
                  }
                  current.WhEntry = refreshed;
                  break;
                }
            }

            session.Save ( value );
            foreach ( var current in entries )
            {
              current.Sale = value;
              current.WhEntry.Quantity -= current.Quantity;
              totalCost += current.Cost;

              session.Update ( current.WhEntry );
              session.Save ( current );
            }

            Location refreshed = session.CreateSQLQuery ( "Select * from Locations where Locations.Id = :lId" )
                                        .AddEntity ( typeof ( Location ) )
                                        .SetInt64 ( "lId", value.Location.Id )
                                        .List<Location> ( )
                                        .FirstOrDefault ( );
            if ( refreshed == null )
            {
              throw new FaultException<InvalidOperationException> ( new InvalidOperationException (
                string.Format ( "Локация {0} не является действительной", value.Location.Name ) ) );
            }

            refreshed.Cash += totalCost;
            session.Update ( refreshed );
            value.Location = refreshed;

            transaction.Commit ( );
            session.Flush ( );
        }
      }
      return value;
}

  [DataContract]
  public class Sell
  {
    [DataMember]
    public virtual long Id
    {
      get;
      set;
    }

    [DataMember]
    public virtual DateTime Date
    {
      get;
      set;
    }

    [DataMember]
    public virtual User Saller
    {
      get;
      set;
    }

    [DataMember]
    public virtual Price Price
    {
      get;
      set;
    }

    [DataMember]
    public virtual Location Location
    {
      get;
      set;
    }
  }

  [DataContract]
  public class SellEntry
  {
    [DataMember]
    public virtual long Id
    {
      get;
      set;
    }

    [DataMember]
    public virtual Sell Sell
    {
      get;
      set;
    }

    [DataMember]
    public virtual Product Product
    {
      get;
      set;
    }

    [DataMember]
    public virtual int Quantity
    {
      get;
      set;
    }

    [DataMember]
    public virtual decimal Cost
    {
      get;
      set;
    }

    [DataMember]
    public virtual WarehouseEntry WhEntry
    {
      get;
      set;
    }
  }
Помните!!! ваш говнокод кому-то предстоит разгребать.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.