0

I have table Player and table Statistic and other tables that are not important in this question. Table Player has PK Player_ID and it is FK in table Statistic. The relationship between these tables is one-to-many (one player can have more statistics).

Here is the code: GenericRepository(I have created it to have unique class for CRUD methods)

public async Task<int> Delete<T>(Guid id) where T : class
{
        try
        {
            T entity = await Get<T>(id);
            Context.Set<T>().Remove(entity);
            return await Context.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            throw ex;
        }
}

PlayerRepository(for managing operations on Player table)

public async Task<int> Delete(Guid id)
{
        try
        {
            var player = await GenRepository.Get<Player>(id);
            if (player == null)
            {
                return 404;
            }
            else
            {
                return await GenRepository.Delete(player);
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
}

PlayerService(connection between repository and controller in WebAPI)

public async Task<int> Delete(Guid id)
{
        try
        {
            return await PlayerRepository.Delete(id);
        }
        catch (Exception ex)
        {
            throw ex;
        }
}

PlayerController

[HttpDelete]
    [Route("deleteplayer")]
    public async Task<HttpResponseMessage> Delete(Guid id)
    {
        try
        {
            var Finder = Mapper.Map<PlayerView>(await PlayerService.Get(id));
            if(Finder == null)
            {
                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Player doesn't exist in database.");
            }

            var Response = await PlayerService.Delete(id);
            var profile = "../../../app/pictures/FootballFanAppPictures/" + Finder.Club_ID.ToString().ToUpper() + "/profiles/" + id.ToString().ToUpper() + ".png";
            var details = "../../../app/pictures/FootballFanAppPictures/" + Finder.Club_ID.ToString().ToUpper() + "/" + id.ToString().ToUpper() + ".png";
            if (System.IO.File.Exists(profile))
            {
                System.IO.File.Delete(profile);
            }
            if (System.IO.File.Exists(details))
            {
                System.IO.File.Delete(details);
            }
            return Request.CreateResponse(HttpStatusCode.OK, Response);
        }
        catch(Exception ex)
        {
            return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
        }
    }

Entity models:

-database models

 public partial class Player
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Player()
    {
        this.Statistic = new HashSet<Statistic>();
    }

    public System.Guid Player_ID { get; set; }
    public System.Guid Club_ID { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public double Height { get; set; }
    public int Weight { get; set; }
    public System.DateTime BirthDate { get; set; }
    public string Nationality { get; set; }
    public string Position { get; set; }
    public int Shirtnmbr { get; set; }

    public virtual Club Club { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<Statistic> Statistic { get; set; }
}
using System;
using System.Collections.Generic;

public partial class Statistic
{
    public System.Guid Statistic_ID { get; set; }
    public System.Guid Player_ID { get; set; }
    public int Goals { get; set; }
    public int Assists { get; set; }
    public int FoulsFor { get; set; }
    public int FoulsAgainst { get; set; }
    public int ShotsTotal { get; set; }
    public int ShotsGoal { get; set; }

    public virtual Player Player { get; set; }
}

-domain models (used in repository)

public class PlayerDomain : IPlayerDomain
{
    public Guid Player_ID { get; set; }
    public Guid Club_ID { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public double Height { get; set; }
    public int Weight { get; set; }
    public DateTime BirthDate { get; set; }
    public string Nationality { get; set; }
    public string Position { get; set; }
    public int Shirtnmbr { get; set; }
    public virtual ICollection<IStatisticDomain> Statistic { get; set; }
}
public class StatisticDomain: IStatisticDomain
{
    public Guid Statistic_ID { get; set; }
    public Guid Player_ID { get; set; }
    public int Goals { get; set; }
    public int Assists { get; set; }
    public int FoulsFor { get; set; }
    public int FoulsAgainst { get; set; }
    public int ShotsTotal { get; set; }
    public int ShotsGoal { get; set; }
}

-view models (used in controller)

public class PlayerView
{
    public Guid Player_ID { get; set; }
    public Guid Club_ID { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public double Height { get; set; }
    public int Weight { get; set; }
    public DateTime BirthDate { get; set; }
    public string Nationality { get; set; }
    public string Position { get; set; }
    public int Shirtnmbr { get; set; }
    public virtual ICollection<StatisticView> Statistic { get; set; }
}
public class StatisticView
{
    public Guid Statistic_ID { get; set; }
    public Guid Player_ID { get; set; }
    public int Goals { get; set; }
    public int Assists { get; set; }
    public int FoulsFor { get; set; }
    public int FoulsAgainst { get; set; }
    public int ShotsTotal { get; set; }
    public int ShotsGoal { get; set; }
}

Every class is in a separate file. I use database first approach so i got .edmx file along with database models. Database is created in SQL Server Management Studio.

I can update Player but when I try to delete it 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.

I have searched various answers on google and stackoverflow but I couldn't find an answer that solves my problem

user7479651
  • 689
  • 1
  • 5
  • 14
  • From exception message it looks like Player has relation to another table where you are using player id – Ghulam Mohayudin Aug 29 '17 at 11:24
  • @user7479651,Please refer following links: https://stackoverflow.com/questions/5538974/the-relationship-could-not-be-changed-because-one-or-more-of-the-foreign-key-pro https://stackoverflow.com/questions/19325473/ef6-0-the-relationship-could-not-be-changed-because-one-or-more-of-the-foreign https://stackoverflow.com/questions/32675411/the-relationship-could-not-be-changed-because-one-or-more-of-the-foreign-key-pro – VIGNESH ARUNACHALAM Aug 29 '17 at 11:26
  • @GhulamMohayudin I know that. I don't know how can I delete Statistic entries when I delete specific player. – user7479651 Aug 29 '17 at 11:34
  • @VIGNESHARUNACHALAM I have already seen these answers. The problem is that in these answers they are manipulating directly with db context and I'm not. – user7479651 Aug 29 '17 at 11:36

3 Answers3

0

I don't see Statistic deletion on Player delete in your code. Before you can delete a Player, you need to delete all Player related Statistics first. If there is any left, a player will fail on delete operation.

tgralex
  • 794
  • 4
  • 14
  • Is there any way of deleting Statistics along with Player when I'm deleting Player entry? – user7479651 Aug 29 '17 at 11:31
  • If you want this happening automatically, you can set "ON DELETE CASCADE" flag on FK relationship. Or in Player.Delete() method you need to delete manually all related Statistics, list of which will be in related entities list called by name of your FK. – tgralex Aug 29 '17 at 11:36
  • I have read about cascade deleting. I have set that type of relationship in SSMS on Player table, recreated models and there was no change. I believe that I have set up cascade deleting in a wrong way. – user7479651 Aug 29 '17 at 11:39
  • In DataBase first scenario as you described, the cascade deleting happens on a server side. Does it still fail on Player delete after you set "ON DELETE CASCADE" flag? – tgralex Aug 29 '17 at 11:52
  • I have set up cascade delete as shown in picture in answer above and updated models in visual studio. Do I need to do anything else to make cascade delete to work? Is my way of setting up cascade delete the right way? – user7479651 Aug 29 '17 at 12:11
  • Yes, that is the correct way of doing it. Still getting the same error on Player.Delete()? – tgralex Aug 29 '17 at 12:34
  • I have managed to make it work like @BadDub suggested but I can't delete player and statistic in one go. – user7479651 Aug 29 '17 at 13:21
0

Before you call this line:

var Response = await PlayerService.Delete(id);

You are going to need to retrieve a list of your statistics that are assigned to the player you are trying to delete.

Then loop trough each of the statistics and delete those from your database first.

var stats = Finder.Statistic.ToList();

if(stats != null && stats.Any())
{
   foreach(var stat in stats)
   { 
      //retrieve your stat record here from the database
      //check that the stat record is not null
      //delete your stat record here
   }
}

Then you should be able to delete your player record as there will no longer be references to it.

OR

You could just set ON DELETE CASCADE to true, but you need to be careful that you fully understand what all will be deleted on player deletion.

Bad Dub
  • 1,503
  • 2
  • 22
  • 52
  • 1
    OMG. So simple. I will try that now. It makes sense and I couldn't get that. Thank you :) – user7479651 Aug 29 '17 at 11:40
  • If I do it like this: `var x = await StatisticService.Delete(stat.Statistic_ID);` I get an error "Object reference not set to an instance of an object". – user7479651 Aug 29 '17 at 12:31
  • Its hard to know what this is referring to when I cant debug the code. Are you sure the StatisticService is initialised? Did you check that stat is not null before passing it into the Delete? – Bad Dub Aug 29 '17 at 12:34
  • Also when you map your player record to your player view record are the stats being mapped also? – Bad Dub Aug 29 '17 at 12:35
  • I get stat with complete data and it is not null. To me it doesn't makes sense why it is showing that error. How do you mean are stats being mapped also? They are mapped separately like this: `CreateMap().PreserveReferences().ReverseMap().PreserveReferences(); CreateMap().PreserveReferences().ReverseMap().PreserveReferences(); CreateMap().PreserveReferences().ReverseMap().PreserveReferences(); CreateMap().PreserveReferences().ReverseMap().PreserveReferences();` – user7479651 Aug 29 '17 at 12:39
  • Is StatisticService initialised? – Bad Dub Aug 29 '17 at 12:41
  • yes, it is using interface. `protected IStatisticService StatisticService { get; set; }` – user7479651 Aug 29 '17 at 12:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/153135/discussion-between-bad-dub-and-user7479651). – Bad Dub Aug 29 '17 at 12:44
  • I dont know why you would be getting that error then. Have you tried debugging line by line to see what object is null? – Bad Dub Aug 29 '17 at 12:46
  • When the code gets to line `var x = await StatisticService.Delete(stat.Statistic_ID);` then it fires that error. stat is not null. – user7479651 Aug 29 '17 at 12:51
  • See that makes it look like StatisticService has not been initialised if stat is not null. If you debug again and on that hover over StatisticService and double check its not null. – Bad Dub Aug 29 '17 at 12:52
  • Yes, I see it is null. But why? I have set it like this: `protected IStatisticService StatisticService { get; set; }` – user7479651 Aug 29 '17 at 12:54
  • I have found error. So stupid. I forgot to add it in constructor (singleton pattern) -.- Now I will fix that and try again to debug. – user7479651 Aug 29 '17 at 12:55
  • Well I have managed to delete a player, but in first button click it deletes statistic and throws an error from question for deleting player. In second click it deletes player with no problem. – user7479651 Aug 29 '17 at 13:08
  • You may have to retrieve the player record again before deleting it as changes have been made to its relations. Give that a go. – Bad Dub Aug 29 '17 at 14:10
  • 1
    I have done it with two modals in two .html files with help of two components (I use Angular). First deletes statistic, second deletes player. Your answer is accepted as correct :) – user7479651 Aug 29 '17 at 16:04
0

Set PrimaryKey and Foreignkey Relationship :according to @Bad Dub suggestion.

enter image description here