Taher El Marengoze Ответов: 2

[C#] взаимодействие между пользователями приложения winforms


Я пытаюсь разработать приложение WinForms, приложение представляет собой систему закупок, в которой сотрудники находятся в разных местах (САЙТ) отправка заявок на закупку, содержащих операции, которые должны быть выполнены сотрудником отдела закупок(ХО).

Я использую c# и Entity Framework 6 (подход Database-First) для подключения к базе данных SQL Server, но к триггерам базы данных моих знаний и SqlDependency не работайте на EF так что когда пользователь (САЙТ) отправьте запрос на (ХО) получает уведомление.

Поскольку это приложение winForms, экземпляры (пользователи) не могут общаться друг с другом, я искал по всему интернету и обнаружил, что если мое приложение является веб-приложением, то это может быть легко, но у меня есть базовые знания для этого. ASP.NET.

Я также искал веб-сервис/API (не знаю, являются ли они одинаковыми или разными), но позволит ли такой сервис/API пользователям моего приложения общаться друг с другом.

Что я уже пробовал:

Я попробовал SqlDependency, но он не работает для Entity Framework.

using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System;
using System.Data.Entity.Core.Objects;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq.Expressions;
using System.Threading;
using System.Collections.Concurrent;

namespace SQL_Dependency_Test
{
    public static class DbQueryExtension
    {
        public static ObjectQuery<T> ToObjectQuery<T>(this DbQuery<T> query)
        {
            var internalQuery = query.GetType()
                .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
                .Where(field => field.Name == "_internalQuery")
                .Select(field => field.GetValue(query))
                .First();

            var objectQuery = internalQuery.GetType()
                .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
                .Where(field => field.Name == "_objectQuery")
                .Select(field => field.GetValue(internalQuery))
                .Cast<ObjectQuery<T>>()
                .First();

            return objectQuery;
        }

        public static SqlCommand ToSqlCommand<T>(this DbQuery<T> query)
        {
            SqlCommand command = new SqlCommand();

            command.CommandText = query.ToString();

            var objectQuery = query.ToObjectQuery();

            foreach (var param in objectQuery.Parameters)
            {
                command.Parameters.AddWithValue(param.Name, param.Value);
            }

            return command;
        }

        public static string ToTraceString<T>(this DbQuery<T> query)
        {
            var objectQuery = query.ToObjectQuery();

            return objectQuery.ToTraceStringWithParameters();
        }

        public static string ToTraceStringWithParameters<T>(this ObjectQuery<T> query)
        {
            string traceString = query.ToTraceString() + "\n";

            foreach (var parameter in query.Parameters)
            {
                traceString += parameter.Name + " [" + parameter.ParameterType.FullName + "] = " + parameter.Value + "\n";
            }

            return traceString;
        }
    }

    public class EntityChangeEventArgs<T> : EventArgs
    {
        public IEnumerable<T> Results { get; set; }
        public bool ContinueListening { get; set; }
    }

    public class EntityChangeNotifier<TEntity, TDbContext>
        : IDisposable
        where TDbContext : DbContext, new()
        where TEntity : class
    {
        private DbContext _context;
        private Expression<Func<TEntity, bool>> _query;
        private string _connectionString;

        public event EventHandler<EntityChangeEventArgs<TEntity>> Changed;
        public event EventHandler<NotifierErrorEventArgs> Error;

        public EntityChangeNotifier(Expression<Func<TEntity, bool>> query)
        {
            _context = new TDbContext();
            _query = query;
            _connectionString = _context.Database.Connection.ConnectionString;

            SafeCountDictionary.Increment(_connectionString, x => { SqlDependency.Start(x); });

            RegisterNotification();
        }

        private void RegisterNotification()
        {
            _context = new TDbContext();

            using (SqlConnection connection = new SqlConnection(_connectionString))
            {
                using (SqlCommand command = GetCommand())
                {
                    command.Connection = connection;
                    connection.Open();

                    var sqlDependency = new SqlDependency(command);
                    sqlDependency.OnChange += new OnChangeEventHandler(_sqlDependency_OnChange);

                    // NOTE: You have to execute the command, or the notification will never fire.
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                    }
                }
            }
        }

        private string GetSql()
        {
            var q = GetCurrent();

            return q.ToTraceString();
        }

        private SqlCommand GetCommand()
        {
            var q = GetCurrent();

            return q.ToSqlCommand();
        }

        private DbQuery<TEntity> GetCurrent()
        {
            var query = _context.Set<TEntity>().Where(_query) as DbQuery<TEntity>;

            return query;
        }

        private void _sqlDependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            if (_context == null)
                return;

            if (e.Type == SqlNotificationType.Subscribe || e.Info == SqlNotificationInfo.Error)
            {
                var args = new NotifierErrorEventArgs
                {
                    Reason = e,
                    Sql = GetCurrent().ToString()
                };

                OnError(args);
            }
            else
            {
                var args = new EntityChangeEventArgs<TEntity>
                {
                    Results = GetCurrent(),
                    ContinueListening = true
                };

                OnChanged(args);

                if (args.ContinueListening)
                {
                    RegisterNotification();
                }
            }
        }

        protected virtual void OnChanged(EntityChangeEventArgs<TEntity> e)
        {
            if (Changed != null)
            {
                Changed(this, e);
            }
        }

        protected virtual void OnError(NotifierErrorEventArgs e)
        {
            if (Error != null)
            {
                Error(this, e);
            }
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                SafeCountDictionary.Decrement(_connectionString, x => { SqlDependency.Stop(x); });

                if (_context != null)
                {
                    _context.Dispose();
                    _context = null;
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }

    public class NotifierErrorEventArgs : EventArgs
    {
        public string Sql { get; set; }
        public SqlNotificationEventArgs Reason { get; set; }
    }

    public static class SafeCountDictionary
    {
        private class SafeCount : MarshalByRefObject
        {
            private int _counter;
            public int Counter { get { return _counter; } }

            public void Increment()
            {
                Interlocked.Increment(ref _counter);
            }

            public void Decrement()
            {
                Interlocked.Decrement(ref _counter);
            }
        }

        private static ConcurrentDictionary<string, SafeCount> _registeredConnectionStrings = new ConcurrentDictionary<string, SafeCount>();

        /// <summary>
        /// Increments the count for the given key by one. If the key does not exist, it creates it and sets the count to 1.
        /// </summary>
        /// <param name="key">The key to increment.</param>
        /// <param name="onAdd">Executes when the key is first created. Does not run on updates.</param>
        public static void Increment(string key, Action<string> onAdd)
        {
            var safeCount = _registeredConnectionStrings.GetOrAdd(key, x =>
            {
                onAdd(x);
                return new SafeCount();
            });

            safeCount.Increment();
        }

        /// <summary>
        /// Decrements the count for the given key by one. If the count reaches 0, it runs <paramref name="onZero"/>.
        /// </summary>
        /// <param name="key">The key to decrement.</param>
        /// <param name="onZero">Executes when the count equals zero.</param>
        public static void Decrement(string key, Action<string> onZero)
        {
            SafeCount val;

            if (_registeredConnectionStrings.TryGetValue(key, out val))
            {
                val.Decrement();

                if (val.Counter == 0)
                {
                    onZero(key);
                }
            }
        }
    }

}


В классе тестовой формы это код который я написал

private void Form1_Load(object sender, EventArgs e)
        {
            textBox2.Text = ConfigurationManager.ConnectionStrings["TestEntities"].ConnectionString;

            using (var notifer = new EntityChangeNotifier<Employee, TestEntities>(p=>p.employeeName == "John Doe"/*id != null*/))
            {
                notifer.Error += (sender2, e2) =>
                {
                    Console.WriteLine("[{0}, {1}, {2}]:\n{3}", e2.Reason.Info, e2.Reason.Source, e2.Reason.Type, e2.Sql);
                };

                notifer.Changed += (sender2, e2) =>
                {
                    Console.WriteLine(e2.Results.Count());
                    foreach (var p in e2.Results)
                    {
                        textBox2.AppendText(p.id + ":" + p.employeeName);
                    }
                };
            }
        }

Dave Kreskowiak

Что заставляет вас думать, что все, что вы делаете с базой данных, должно быть сделано через EF?

Вы можете использовать EF и использовать триггеры и SqlDependency одновременно.

Taher El Marengoze

Это хорошо, не могли бы вы указать мне на некоторые примеры или сообщения по этому поводу.

Dave Kreskowiak

Все, что вам нужно сделать, это Google для "C# sqldependency[^]"

2 Ответов

Рейтинг:
2

johannesnestler

Почему вы думаете, что SQLDependency не будет работать с EF? (Я не проверял EF 6, но тогда это сработало для меня:

SqlDependency с Entity Framework 5.0[^]


Taher El Marengoze

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

Рейтинг:
0

Taher El Marengoze

Ну я и попробовал Зависимость SQL с использованием Entity Framework в WPF[^] и это сработало как заклинание.

Но я не знаю, есть ли у него какие-то ограничения или нет, думаю, Время покажет.

В любом случае, спасибо всем за ваши ответы.