4

Currently we use the UnitOfWork design pattern which we cannot change. The generic repository class looks like this:

public class Repository<T> : IDisposable, IRepository<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 Repository(DbContext context)
    {

        // If no context is supplied, throw an error
        if (context == null)
            throw new ArgumentNullException("context");

        // Assign our context and entity set
        this.context = context;
        this.dbEntitySet = context.Set<T>();
    }

    /// <summary>
    /// Allows the execution of stored procedures
    /// </summary>
    /// <typeparam name="T">The entity model</typeparam>
    /// <param name="sql">The name of the stored procedure</param>
    /// <param name="parameters">Option list of parameters</param>
    /// <returns></returns>
    public DbRawSqlQuery<T> SQLQuery(string sql, params object[] parameters)
    {
        return this.context.Database.SqlQuery<T>(sql, parameters);
    }

    /// <summary>
    /// Gets all the entities
    /// </summary>
    /// <param name="includes">Option includes for eager loading</param>
    /// <returns></returns>
    public IQueryable<T> GetAll(params string[] includes)
    {
        IQueryable<T> query = this.dbEntitySet;
        foreach (var include in includes)
            query = query.Include(include);

        return query;
    }

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

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

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

    /// <summary>
    /// Dispose method
    /// </summary>
    public void Dispose()
    {
        this.context.Dispose();
    }

and the service looks like this:

public class Service<T> where T : class
{
    private readonly IUnitOfWork unitOfWork;
    private readonly IRepository<T> repository;

    protected IUnitOfWork UnitOfWork
    {
        get { return this.unitOfWork; }
    }

    protected IRepository<T> Repository
    {
        get { return this.repository; }
    }

    public Service(IUnitOfWork unitOfWork)
    {
        if (unitOfWork == null)
            throw new ArgumentNullException("unitOfWork");

        this.unitOfWork = unitOfWork;
        this.repository = unitOfWork.GetRepository<T>();
    }
}

and the UnitOfWork class looks like this:

/// <summary>
/// Used to handle the saving of database changes
/// </summary>
/// <typeparam name="TContext"></typeparam>
public class UnitOfWork<TContext> : IUnitOfWork where TContext : DbContext, new()
{

    // Private properties
    private readonly DbContext context;
    private Dictionary<Type, object> repositories;

    // Public properties
    public DbContext Context { get { return this.context; } }

    /// <summary>
    /// Default constructor
    /// </summary>
    public UnitOfWork()
    {
        this.context = new TContext();
        repositories = new Dictionary<Type, object>();
    }

    /// <summary>
    /// Gets the entity repository
    /// </summary>
    /// <typeparam name="TEntity">The entity model</typeparam>
    /// <returns></returns>
    public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
    {

        // If our repositories have a matching repository, return it
        if (repositories.Keys.Contains(typeof(TEntity)))
            return repositories[typeof(TEntity)] as IRepository<TEntity>;

        // Create a new repository for our entity
        var repository = new Repository<TEntity>(context);

        // Add to our list of repositories
        repositories.Add(typeof(TEntity), repository);

        // Return our repository
        return repository;
    }

    /// <summary>
    /// Saves the database changes asynchronously
    /// </summary>
    /// <returns></returns>
    public async Task SaveChangesAsync()
    {
        try
        {

            // Save the changes to the database
            await this.context.SaveChangesAsync();

            // If there is an error
        } 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);
        }
    }

    /// <summary>
    /// Dispose
    /// </summary>
    public void Dispose()
    {
        this.context.Dispose();
    }
}

Now I have a ProductService which looks like this:

/// <summary>
/// Handles all product related methods
/// </summary>
public class ProductService : Service<Product>
{

    /// <summary>
    /// The default constructor
    /// </summary>
    /// <param name="unitOfWork"></param>
    public ProductService(IUnitOfWork unitOfWork)
        : base(unitOfWork)
    {
    }

    /// <summary>
    /// Gets all products
    /// </summary>
    /// <param name="includes">Optional eager loading includes</param>
    /// <returns></returns>
    public async Task<List<Product>> GetAllAsync(params string[] includes)
    {

        // Return as an asynchronous list
        return await this.Repository.GetAll(includes).ToListAsync();
    }

    /// <summary>
    /// Gets a single product by id
    /// </summary>
    /// <param name="id">The id of the product</param>
    /// <param name="includes">Optional eager loading includes</param>
    /// <returns></returns>
    public async Task<Product> GetAsync(int id, params string[] includes)
    {
        var models = await this.GetAllAsync(includes);
        return models.Where(model => model.Id == id).SingleOrDefault();
    }

    /// <summary>
    /// Create a product
    /// </summary>
    /// <param name="model">The product model</param>
    public void Create(Product model)
    {

        // Create a team
        this.Repository.Create(model);
    }

    /// <summary>
    /// Update a product
    /// </summary>
    /// <param name="model">The product model</param>
    public void Update(Product model)
    {

        // Update a team
        this.Repository.Update(model);
    }

    /// <summary>
    /// Delete a product
    /// </summary>
    /// <param name="model">The product model</param>
    public void Remove(Product model)
    {

        // Remove a team
        this.Repository.Remove(model);
    }
}

I have set up my Code First database to use Cascading Delete when deleting a product. There are quite a few 1 to 1 table relationships set up. If I use SQL Management Studio I can manually delete a product from the database and it will delete all the related entries. But, if I try to delete it from my application I get this error:

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.

Ideally I would like to loop through the children of a generic class (so in the Reposity class) and check to see if cascading delete is enabled for that entity, if it is I would like to delete it. If not, do nothing.

Does anyone know if that is possible or know of another solution that does not involve dropping the UnitOfWork design pattern?

r3plica
  • 13,017
  • 23
  • 128
  • 290
  • take a look at [Cascading deletes with Entity Framework - Related entities deleted by EF](http://stackoverflow.com/a/5448843/5233410) – Nkosi May 03 '16 at 15:18
  • also [Entity Framework (EF) Code First Cascade Delete for One-to-Zero-or-One relationship](http://stackoverflow.com/a/17487948/5233410) – Nkosi May 03 '16 at 15:25

0 Answers0