开发者

.NET Entity Framework Connection Failure

I just recently started learning and using the ADO.NET Entity Framework and ran into a few problems. Sometimes my Wifi connection to the router breaks so I cannot connect to the database which is on another computer in the network. The failed connection causes my whole application to freeze for about 20 seconds and then throw an exception. I want to catch the exception and show a custom error message instead, however I don't want to use try-catch around every query.

Here is what I tried to do. I created a static method to create a context every time I need one and wrapped it inside a try-catch statement to try and catch the connection error and not move on until there is a connection or the user answers No to the MessaeBox and exits the application.

public static MySqlEntities Database
{
    get
    {
        try
        {
            // create a new context
            MySqlEntities db = new MySqlEntities();

            // return it upon success
            return db;
        }
        catch (Exception ex)
        {
            // show error message upon failute
            MessageBoxResult result = MessageBox.Show("Failed to establish a connection with the database. Please verify that the database server is online, would you like to try again?", "Database Connection Failure", MessageBoxButton.YesNo);

            // close the application if they don't wanna try again
            if (result == MessageBoxResult.No)
            {
                Fx.Window.Close();
                return null;
            }

            // otherwise try again
            return Fx.Database;
        }
    }
}

Here's the repository class I wrote to select,update and add data to/from the database.

public class EmployeeRepository
{
    #region SelectQuery
    /// <summary>
    /// Compiled query for selecting a range of Employees.
    /// </summary>
    protected static readonly Func<MySqlEntities, int, int, IQueryable<Employee>> SelectQuery =
        CompiledQuery.Compile<MySqlEntities, int, int, IQueryable<Employee>>(
            (db, start, limit) =>
            (from t in db.Employees orderby t.ID select t).Skip(start).Take(limit)
        );
    #endregion

    #region SelectyByIDQuery
    /// <summary>
    /// Compiled query for selecting a single Employee by ID.
    /// </summary>
    protected static readonly Func<MySqlEntities, int, Employee> SelectByIDQuery =
        CompiledQuery.Compile<MySqlEntities, int, Employee>(
            (db, id) =>
            (from t in db.Employees where t.ID == id select t).FirstOrDefault()
        );
    #endregion

    #region SelectByUsernameQuery
    /// <summary>
    /// Compiled query for selecting a single Employee by Username.
    /// </summary>
    protected static readonly Func<MySqlEntities, string, Employee> SelectByUsernameQuery =
        CompiledQuery.Compile<MySqlEntities, string, Employee>(
            (db, username) =>
            (from t in db.Employees where t.Username == username select t).FirstOrDefault()
        );
    #endregion

    #region SearchQuery
    /// <summary>
    /// Compiled query for searching Employees by Name and Username
    /// </summary>
    protected static readonly Func<MySqlEntities, string, int, IQueryable<Employee>> SearchQuery =
        CompiledQuery.Compile<MySqlEntities, string, int, IQueryable<Employee>>(
            (db, search, limit) =>
            (from t in db.Employees where t.Name.StartsWith(search) || t.Username.StartsWith(search) select t).Take(limit)
        );
    #endregion

    /// <summary>
    /// Select a range of Employees start at a specific offset.
    /// </summary>
    /// <param name="start">The starting position.</param>
    /// <param name="limit">The maximum number of employees to select.</param>
    /// <returns></returns>
    public static List<Employee> Select(int start = 0, int limit = 10)
    {
        using (var db = Fx.Database)
            return new List<Employee>(SelectQuery.Invoke(db, start, limit));
    }

    /// <summary>
    /// Select a single Employee with a matching ID.
    /// </summary>
    /// <param name="id">The ID to search for.</param>
    /// <returns></returns>
    public static Employee SelectByID(int id)
    {
        using (var db = Fx.Database)
            return SelectByIDQuery.Invoke(db, id);
    }

    /// <summary>
    /// Select a single Employee with a matching Username.
    /// </summary>
    /// <param name="username">The Username to search for.</param>
    /// <returns></returns>
    public static Employee SelectByUsername(string username)
    {
        using (var db = Fx.Database)
            return SelectByUsernameQuery.Invoke(db, username);
    }

    /// <summary>
    /// Search for Employees by Name and Username.
    /// </summary>
    /// <param name="search">The search string.</param>
    /// <param name="limit">The maximum number of Employees to select.</param>
    /// <returns></returns>
    public static List<Employee> Search(string search, int limit = 10)
    {
        using (var db = Fx.Database)
            return new List<Employee>(SearchQuery.Invoke(db, search, limit));
    }


    /// <summary>
    /// Save changes to an Employee to the database.
    /// </summary>
    /// <param name="employee">The Employee object to save.</param>
    public static bool Save(Employee employee)
    {
        using(var db = Fx.Database)
        {
            db.Employees.Attach(employee);
            db.Employees.Context.ObjectStateManager.ChangeObjectState(employee, System.Data.EntityState.Modified);
            try
            {
                db.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to save employee:\n\n" + ex.InnerException.Message);
                return false;
            }
        }
    }

    /// <summary>
    /// Add an Employee to the database.
    /// </summary>
    /// <param name="employee">The Employee object to add.</param>
    public static bool Add(Employee employee)
    {
        using (var db = Fx.Database)
        {
            db.Employees.AddObject(employee);
            try
            {
                db.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to add employee:\n\n" + ex.InnerException.Message);
                return false;
            }
        }
    }
}

Here's an example of how I'm using the EmployeeRepository class...

Employee Employee = EmployeeRepository.SelectByUsername(UsernameInput.Text);
if(Employee =开发者_JS百科= null || Employee.Password != PasswordInput.Password)
    MessageBox.Show("Invalid login credentials.");
else
    MessageBox.Show("Logged in successfully.");

The problem is that this does not work because the exception is thrown afterwards when the query is being executed and not when I create the context.

So my question....

How can I catch the connection error when the context is created and display my custom error message.


Two things:

  1. If you don't want the app to hang up, you should be doing the data operations on a background thread. Consider using a BackgroundWorker.

  2. To detect if the connection is available, you could do:

    a. Pass in your own EntityConnection for the ObjectContext to use (which you've already testing/tried to open in a try catch block). http://msdn.microsoft.com/en-us/library/bb738461.aspx

    b. Manually call myObjectContext.Connection.Open after you create the ObjectContext to test the connection.

    c. Wrap the ObjectContext's queryable/query provider with your own that wraps the IQueryProvider.Execute method with a try/catch block to handle your disconnected client scenario (not recommended for beginners).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜