2017-08-13 256 views
0

我使用EF,但禁用了延遲加載。相反,我使用預先加載,所以我創建了自己的服務:實體框架:刪除子實體

/// <summary> 
/// Generic service for entity framework 
/// </summary> 
/// <typeparam name="T">An entity model</typeparam> 
public class Service<T> : IService<T> where T : class 
{ 
    // Create our private properties 
    private readonly DbContext _context; 
    private readonly DbSet<T> _dbEntitySet; 

    /// <summary> 
    /// Default constructor 
    /// </summary> 
    /// <param name="context">The database context</param> 
    public Service(DbContext context) 
    { 
     // Assign our context and entity set 
     _context = context ?? throw new ArgumentNullException("context"); 
     _dbEntitySet = context.Set<T>(); 
    } 

    /// <summary> 
    /// Gets all the entities 
    /// </summary> 
    /// <param name="includes">Option includes for eager loading</param> 
    /// <returns></returns> 
    public IQueryable<T> List(params string[] includes) 
    { 

     // Create a query 
     IQueryable<T> query = _dbEntitySet; 

     // For each include, append to our query 
     if (includes != null) 
      foreach (var include in includes) 
       query = query.Include(include); 

     // Return our query 
     return query; 
    } 

    /// <summary> 
    /// Creates an entity 
    /// </summary> 
    /// <param name="model"></param> 
    public void Create(T model) => _dbEntitySet.Add(model); 

    /// <summary> 
    /// Updates an entity 
    /// </summary> 
    /// <param name="model"></param> 
    public void Update(T model) => _context.Entry<T>(model).State = EntityState.Modified; 

    /// <summary> 
    /// Removes an entity 
    /// </summary> 
    /// <param name="model"></param> 
    public void Remove(T model) => _context.Entry<T>(model).State = EntityState.Deleted; 

    /// <summary> 
    /// Saves the database context changes 
    /// </summary> 
    /// <returns></returns> 
    public async Task SaveChangesAsync() 
    { 
     try 
     { 
      // Save the changes to the database 
      await _context.SaveChangesAsync(); 
     } 
     catch (DbEntityValidationException ex) 
     { 

      // Retrieve the error messages as a list of strings. 
      var errorMessages = ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage); 

      // Join the list to a single string. 
      var fullErrorMessage = string.Join("; ", errorMessages); 

      // Combine the original exception message with the new one. 
      var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage); 

      // Throw a new DbEntityValidationException with the improved exception message. 
      throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors); 
     } 
     catch (DbUpdateException ex) 
     { 
      throw; 
     } 
    } 

    /// <summary> 
    /// Executes a stored procedure in sql 
    /// </summary> 
    /// <param name="procedure">The name of the sproc</param> 
    /// <param name="parameters">the sql params for the sproc</param> 
    /// <returns></returns> 
    public DbRawSqlQuery<T> ExecuteProcedure(string procedure, List<SqlParameter> parameters) 
    { 
     var results = _context.Database.SqlQuery<T>($"exec {procedure} { CreateQueryStringFromParams(parameters) }"); 
     return results; 
    } 

    /// <summary> 
    /// Dispose 
    /// </summary> 
    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    /// <summary> 
    /// Creates the input string to run sprocs in sql with EF by converting the sql params into a nice string 
    /// </summary> 
    /// <param name="parameters"></param> 
    /// <returns></returns> 
    private static string CreateQueryStringFromParams(IEnumerable<SqlParameter> parameters) 
    { 
     var response = ""; 
     var list = parameters as IList<SqlParameter> ?? parameters.ToList(); 
     var length = list.Count; 

     for (var i = 0; i < length; i++) 
     { 
      response += $"{list[i].ParameterName}=\"{list[i].Value}\""; 
      if (i != length - 1) 
       response += ", "; 
     } 

     return response; 
    } 

    /// <summary> 
    /// Disposes of any attached resources 
    /// </summary> 
    /// <param name="disposing">A boolean indicating whether the object is being disposed</param> 
    protected virtual void Dispose(bool disposing) 
    { 

     // If we are disposing, dispose of our context 
     if (disposing) 
      _context.Dispose(); 
    } 
} 

每個服務則繼承了這個類:

/// <summary> 
/// Handles all Group related methods 
/// </summary> 
public class GroupService : Service<Group>, IGroupService 
{ 

    /// <summary> 
    /// The default constructor 
    /// </summary> 
    /// <param name="unitOfWork"></param> 
    public GroupService(DbContext context) : base(context) 
    { 
    } 

    /// <summary> 
    /// Lists groups by category 
    /// </summary> 
    /// <param name="categoryId">The id of the category</param> 
    /// <param name="includes"></param> 
    /// <returns></returns> 
    public IQueryable<Group> List(int categoryId, params string[] includes) => List(includes).Where(m => m.CategoryId == categoryId); 

    /// <summary> 
    /// Gets a single Group by id 
    /// </summary> 
    /// <param name="id">The id of the Group</param> 
    /// <returns></returns> 
    public async Task<Group> GetAsync(int id, params string[] includes) => await List(includes).Where(model => model.Id == id).SingleOrDefaultAsync(); 
} 

每個「實體」也有類似的這種GroupService類。 我還爲每個實體類型提供商過,這裏是我的刪除方法:

/// <summary> 
/// Delete a Group 
/// </summary> 
/// <param name="id">The Group id</param> 
/// <returns></returns> 
public async Task<bool> DeleteAsync(int id) 
{ 

    // Get our model 
    var model = await _service.GetAsync(id, "Questions"); 

    // For each question, remove from the database 
    if (model.Questions != null) 
     foreach (var question in model.Questions.ToList()) 
      if (!await _questionProvider.Value.DeleteAsync(question.Id, false)) 
       throw new Exception("Failed to delete the questions"); 

    // Update our Questions 
    model.Questions = null; 

    // Save our model 
    _service.Remove(model); 

    // Save the database changes 
    await _service.SaveChangesAsync(); 

    // Return true 
    return true; 
} 

正如你所看到的,我剛拉回來的問題。

如果有一些問題,然後我調用questionProvider的刪除方法,這是很相似:

/// <summary> 
/// Delete a question 
/// </summary> 
/// <param name="id">The question id</param> 
/// <param name="saveChanges">Saves the changes to the database after the delete (default true)</param> 
/// <returns></returns> 
public async Task<bool> DeleteAsync(int id, bool saveChanges = true) 
{ 

    // Get our model 
    var model = await _service.GetAsync(id, "Answers"); 

    // For each answer, delete from the database 
    if (model.Answers != null) 
     foreach (var answer in model.Answers.ToList()) 
      if (!await _answerProvider.Value.DeleteAsync(answer.Id, false)) 
       throw new Exception("Failed to delete the answers"); 

    // Update our Answers 
    model.Answers = null; 

    // Save our model 
    _service.Remove(model); 

    // Save the database changes 
    if (saveChanges) await _service.SaveChangesAsync(); 

    // Return true 
    return true; 
} 

正如你所看到的,我不救,直到所有的孩子都被刪除了環境的變化。現在我必須指出,我並不是直接從父母那裏取出孩子。相反,我從它自己的集合中刪除實體,然後將該屬性設置爲null。之後,它是所有做我保存更改,但我得到這個錯誤:

The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.

有誰知道我怎麼能以類似的方法刪除實體什麼,我想達到什麼目的?

回答

0

在閱讀了一些其他有類似問題的人之後,這很容易解決。我只是改變了一行代碼在我Service.cs來自:

public void Remove(T model) => _context.Entry<T>(model).State = EntityState.Deleted; 

到:

public void Remove(T model) => _dbEntitySet.Remove(model); 

和起作用的。


更新

我還發現,急於負載需要包括將所有受影響的導航性能。 所以,如果我刪除問題具有答案答案公式,則獲取調用必須是:

var model = await _service.GetAsync(id, "Answers.Formulas"); 

如果不包括,你會得到一個錯誤。