2017-08-13 189 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"); 

如果不包括,你会得到一个错误。