Проблема в c # windows service с использованием linq - PullRequest
2 голосов
/ 30 ноября 2010

У меня возникла небольшая проблема с разработанной мной службой Windows. Кажется, у меня конфликт с БД при использовании linq. Я думаю, что понимаю причину проблемы, но хотел бы получить совет по ее устранению.

У меня есть база данных с таблицей, в которой хранятся сообщения, которые необходимо отправлять каждые несколько раз из службы. Вот мой фрагмент кода.

protected override void OnStart(string[] args)
    {
        timer = new Timer(10000);
        timer.Elapsed += new ElapsedEventHandler(TimeElapsed);
        timer.Interval = 10000;
        timer.Start();
        EventLogger.LogEvent("Timer started succesfuly", EventLogEntryType.Information);

    }

    protected override void OnStop()
    {
        if (timer != null)
            timer.Stop();
        EventLogger.LogEvent("Timer stopped.", EventLogEntryType.Information);
    }

    private void TimeElapsed(object source, ElapsedEventArgs e)
    {
        try
        {
            EventLogger.LogEvent("Checking for reversals", EventLogEntryType.Information);
            //We now need to check for reversals that are waiting to be sent
            ReversalsDataContext db = new ReversalsDataContext();
            var reversals = db.FiReversals.Where(r => r.Status == Reversal.ReversalStatus.WAITING_TO_SEND);

            if (reversals.Any())
            {
                EventLogger.LogEvent(reversals.Count() + " reversals found and being processed.", EventLogEntryType.Information);
                //Mark the reversal
                foreach (var rev in reversals)
                {
                    MarkReversal(rev);
                }
                db.SubmitChanges();



                //We now need to send the reversal
                foreach (var rev in reversals)
                {
                    SendReversal(rev);
                }
                db.SubmitChanges();
                EventLogger.LogEvent("Finished processing reversal queue", EventLogEntryType.Information);
            }
        }
        catch (Exception ex)
        {
            EventLogger.LogEvent(ex.Message + "\n" + ex.StackTrace, EventLogEntryType.Error);
        }
    }

Ошибка, которую я получаю сейчас:

Строка не найдена или не изменена. в System.Data.Linq.ChangeProcessor.SubmitChanges (ConflictMode faultMode) в System.Data.Linq.DataContext.SubmitChanges (ConflictMode faultMode) в System.Data.Linq.DataContext.SubmitChanges () at EftcObReversalService.EftcObReversalService.TimeElapsed (Источник объекта, ElapsedEventArgs e)

Из того, что я вижу, это вызвано следующим кодом,

if (reversals.Any())
            {
                EventLogger.LogEvent(reversals.Count() + " reversals found and being processed.", EventLogEntryType.Information);
                //Mark the reversal
                foreach (var rev in reversals)
                {
                    MarkReversal(rev);
                }
                db.SubmitChanges();



                //We now need to send the reversal
                foreach (var rev in reversals)
                {
                    SendReversal(rev);
                }
                db.SubmitChanges();
                EventLogger.LogEvent("Finished processing reversal queue", EventLogEntryType.Information);
            }

Что происходит здесь, я читаю из таблицы то, что помечено как «WAITING_TO_SEND», как только я получаю, я решаю пометить записи статусом «ОЖИДАНИЕ». Я делаю это, так как не хочу, чтобы записи снова читались, когда таймер переключается, а эта тема еще не завершена.

После того, как я отметил все входы, я обрабатываю каждый из них в методе SendReversal, где я также отмечаю статус как «ЗАВЕРШЕНО», когда он был завершен, или меняю статус обратно на «WAITING_TO_SEND».

Я думаю, что эта ошибка вызвана 2 db.SubmitChanges, так как запись изменилась и была записана первой отправкой и отличается от второй отправки?

Если у кого-то есть мысли или решения, пожалуйста, дайте мне знать.

Ответы [ 3 ]

2 голосов
/ 30 ноября 2010

Я собирался предположить, что самым простым решением было бы запросить «ожидание» ... но, конечно, это фактически не решает проблему, если процесс будет перегружен.

Естьнесколько вариантов, которые я вижу быстро:

  1. Измените логику так, чтобы вы могли запускать только один экземпляр цикла обработки за раз - в основном, если вы уже обрабатываете, вы не можете запуститьтак что пропустите или перепланируйте.
  2. В дополнение к изменению состояния вам также необходимо добавить номер партии, чтобы можно было запрашивать после первого сохранения, но ограничить этот запрос элементами, ожидающими определенного номера партии.делали это в прошлом и работали достаточно хорошо, при условии, что обработка ошибок была достаточно умной, чтобы потом можно было исправить ситуацию, если что-то пошло странно.
1 голос
/ 14 декабря 2010

Хорошо, благодаря совету Мёрфа, я нашел свое решение, вот оно, если кому-то нужна ссылка,

В моем распоряжении у меня есть

private ReversalSender _revSender;
    private Thread _revSenderThread;

    public EftcObReversalService()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        _revSender = new ReversalSender();
        _revSenderThread = new Thread(new ThreadStart(_revSender.Run));
        _revSenderThread.Start();

        var timer = new System.Timers.Timer(5000);
        timer.Elapsed += new ElapsedEventHandler(TimerElapsed);
        timer.Start();
    }

    void TimerElapsed(object sender, ElapsedEventArgs e)
    {
        _revSender.Signal();
    }

    protected override void OnStop()
    {
        _revSender.Stop();
        _revSenderThread.Join(60000);
    }

Теперь, чтобы убедиться, что у нас нет проблем параллелизма при доступе к БД, у ReversalSender есть ManualResetEvent

public class ReversalSender
{
    private bool _running = true;
    private ManualResetEvent _reset = new ManualResetEvent(false);

    public void Run()
    {
        while (_running)
        {
            _reset.WaitOne();
            //We now need to grab all the waiting reversals
            ReversalsDataContext db = new ReversalsDataContext();
            var reversals = db.FiReversals.Where(r => r.Status == Reversal.ReversalStatus.WAITING_TO_SEND);
            if (reversals.Any())
            {
                foreach (var rev in reversals)
                {
                    if (_running)
                        SendReversal(rev);
                }
            }
            db.SubmitChanges();
        }
    }

    private void SendReversal(FiReversal rev)
    {
       .....
    }

    public void Signal()
    {
        _reset.Set();
    }

    public void Stop()
    {
        _running = false;
    }
}
0 голосов
/ 30 ноября 2010

Просто догадка, но я думаю, что вы на правильном пути. Попробуйте удалить первый вызов db.SaveChanges ().

if (reversals.Any())
{   
    foreach (var rev in reversals)
    {
        MarkReversal(rev);
    }       

    //We now need to send the reversal
    foreach (var rev in reversals)
    {
        SendReversal(rev);
    }

    db.SubmitChanges();                
}

EDIT: Не могли бы вы сделать это:

if (reversals.Any())
{   
    foreach (var rev in reversals)
    {
        MarkReversal(rev);
        SendReversal(rev);
    }

    db.SubmitChanges();
}

Выполните два вызова в одном и том же контексте для каждого объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...