我正在使用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。完成所有操作后,我保存更改,但是我收到此错误:
操作失败:无法更改关系,因为一个或多个外键属性不可为空。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。
有谁知道如何删除类似于我想要实现的方法中的实体?
答案 0 :(得分:1)
在阅读其他有类似问题的人之后,这很容易解决。我刚从 Service.cs 中更改了一行代码:
public void Remove(T model) => _context.Entry<T>(model).State = EntityState.Deleted;
到:
public void Remove(T model) => _dbEntitySet.Remove(model);
这很有效。
<强>更新强>
我还发现,急切的负载需要包含所有受影响的导航属性。 因此,如果我要删除问题 答案且答案 公式,获取< / strong>调用必须是:
var model = await _service.GetAsync(id, "Answers.Formulas");
如果您不包含该内容,则会收到错误。