2017-08-13 2 views
0

Ich benutze EF, aber ich habe Lazy Loading deaktiviert. Stattdessen eager loading Ich verwende, so habe ich meine eigene Service:Entity Framework: Löschen von untergeordneten Entitäten

/// <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(); 
    } 
} 

Jeder Dienst dann erbt diese Klasse:

/// <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(); 
} 

Jede "Einheit" hat eine Klasse ähnlich wie diese Group. Ich habe auch Anbieter für jeden Entitätstyp zu und hier ist meine Löschmethode:

/// <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; 
} 

Wie Sie sehen können, habe ich die Fragen einfach zurückziehen.

Wenn es einige Fragen gibt, dann rufe ich die des questionProvider Löschmethode, die sehr ähnlich ist:

/// <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; 
} 

Wie Sie sehen können, ich speichere nicht der Kontext ändert, bis alle Kinder wurden entfernt . Jetzt muss ich darauf hinweisen, dass ich das Kind nicht direkt vom Elternteil entferne. Stattdessen entferne ich die Entität aus ihrer eigenen Sammlung und setze dann die Eigenschaft auf null. Nachdem es ist alles getan, was ich die Änderungen speichern, aber ich bin immer diese Fehlermeldung:

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.

Wer weiß, wie ich die Entitäten in einem Verfahren löschen können, ähnlich dem, was ich erreichen möchte?

Antwort

0

Dies war ziemlich einfach zu beheben nach dem Lesen einiger anderer Leute mit ähnlichen Problemen. Ich habe gerade eine Zeile Code in meinem Service.cs aus:

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

zu:

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

Und das funktionierte.


aktualisieren

Ich fand auch, dass die eifrigen Last alle Navigationseigenschaften umfassen muss, die betroffen sein wird. Also, wenn ich eine Frage die Antworten und Antworten hat löschen warFormeln haben, die Get Aufruf wäre müssen:

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

Wenn Sie nicht, dass beinhalten, Sie erhalten einen Fehler.

Verwandte Themen