Мы пишем приложение для внешней службы, которая передает нам такие данные:

{"businessSystemId":"SE","caseId":1,…,"relatedCases":[{"businessSystemId":"SE","caseId":1,"relationshipNo":123,"relatedBusinessSystemId":"SE","relatedCaseId":2,"…

{"businessSystemId":"SE","caseId":2,…,"relatedCases":[{"businessSystemId":"SE","caseId":2,"relationshipNo":123,"relatedBusinessSystemId":"SE","relatedCaseId":1,"…

Классы фреймворка сущности:

public class TPCase
{
    [Key, Column(Order = 0)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public string BusinessSystemId { get; set; }

    [Key, Column(Order = 1)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int CaseId { get; set; }

    public virtual ICollection<TPRelatedCase> RelatedCases { get; set; }
}

public class TPRelatedCase
{
    [Key, Column(Order = 0)]
    [ForeignKey("TPCase")]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public string BusinessSystemId { get; set; }

    [Key, Column(Order = 1)]
    [ForeignKey("TPCase")]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int CaseId { get; set; }

    [InverseProperty("RelatedCases")]
    public virtual TPCase TPCase { get; set; }

    [Key, Column(Order = 2)]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int RelationshipNo { get; set; }

    [ForeignKey("RelatedCase"), Column(Order = 3)]
    public string RelatedBusinessSystemId { get; set; }

    [ForeignKey("RelatedCase"), Column(Order = 4)]
    public int? RelatedCaseId { get; set; }

    public virtual TPCase RelatedCase { get; set; }
}

Канал данных и классы сокращены, чтобы сосредоточиться на текущей проблеме. Когда я пытаюсь добавить любой из этих случаев, я получаю следующую ошибку:

Оператор INSERT конфликтует с ограничением FOREIGN KEY «FK_dbo.TPRelatedCases_dbo.TPCases_RelatedBusinessSystemId_RelatedCaseId». Конфликт произошел в базе данных «Test.Database», таблица «dbo.TPCases».

Заявление было прекращено.

Это, конечно, понятно с учетом нашей модели, но это означает, что я не могу использовать обычный dbContext.Cases.Add(newCase); ни для одного из объектов выше. Обычно я могу рекурсивно перебирать коллекцию RelatedCases и сначала добавлять внешние ключи, но в этом случае это приводит к infinite loop. Нужно ли мне очищать коллекцию TPCase.RelatedCases и добавлять связи после добавления кейсов, или структура сущностей может как-то решить эту проблему?

0
Ogglas 26 Фев 2018 в 14:14

1 ответ

Лучший ответ

В итоге я использовал HashSet с пользовательским IEqualityComparer, чтобы избежать дублирования для TPRelatedCase. Мне пришлось изменить с ICollection<TPRelatedCase> RelatedCases на IList<TPRelatedCase> RelatedCases в TPCase, чтобы использовать indexer (оператор квадратных скобок массива).

IEqualityComparer:

public class TPRelatedCaseComparer : IEqualityComparer<TPRelatedCase>
{
    public bool Equals(TPRelatedCase x, TPRelatedCase y)
    {
        var xJson = JsonConvert.SerializeObject(x);
        var yJson = JsonConvert.SerializeObject(y);

        return xJson.Equals(yJson);
    }

    public int GetHashCode(TPRelatedCase obj)
    {
        return obj.BusinessSystemId.GetHashCode() + obj.CaseId.GetHashCode() + obj.RelationshipNo.GetHashCode();
    }
}

Метод получения новой информации и добавления связанных случаев на более позднем этапе (AddOrUpdateRelatedCases):

public void AddOrUpdateNewInformation(InformationToGet info)
{
    AddOrUpdatePossibleForeignKeys(info);

    var comparer = new TPRelatedCaseComparer();
    var relatedCases = new HashSet<TPRelatedCase>(comparer);

    _logger.Info($"Started updating cases");
    foreach (var infoCaseKey in info.CaseKeys)
    {
        var newCase = _integrationApiService.GetCase(infoCaseKey.BusinessSystemId, infoCaseKey.CaseId);

        AddOrUpdateCase(newCase, relatedCases);

        AddOrUpdateRelatedCases(relatedCases);
    }
}

Рекурсивный метод для добавления всех случаев, но не связанных случаев.

private HashSet<TPRelatedCase> AddOrUpdateCase(TPCase newCase, HashSet<TPRelatedCase> relatedCases)
{
    if (newCase?.RelatedCases?.Count > 0)
    {
        relatedCases.UnionWith(newCase.RelatedCases);

        foreach (var relatedCase in newCase.RelatedCases)
        {
            if (relatedCase.RelatedCaseId.HasValue)
            {
                var newRelatedCase = _integrationApiService.GetCase(relatedCase.RelatedBusinessSystemId, relatedCase.RelatedCaseId.Value);

                for (int i = 0; i < newRelatedCase.RelatedCases.Count - 1; i++)
                {
                    if (relatedCases.Any(x =>
                        x.BusinessSystemId == newRelatedCase.RelatedCases[i].BusinessSystemId && x.CaseId ==
                        newRelatedCase.RelatedCases[i].CaseId && x.RelationshipNo == newRelatedCase.RelatedCases[i].RelationshipNo))
                    {
                        newRelatedCase.RelatedCases.Remove(newRelatedCase.RelatedCases[i]);
                    }
                }

                relatedCases = AddOrUpdateCase(newRelatedCase, relatedCases);
            }
        }
    }
    //Need to clear RelatedCases due to the fact that circular references could happen and therefore a recursive method is not possible. 
    //Would lead to FOREIGN KEY constraint exception.
    newCase.RelatedCases = null;

    var current = _dbContext.Cases.Find(newCase.BusinessSystemId, newCase.CaseId);
    if (current == null)
    {
        _dbContext.Cases.Add(newCase);
    }
    else
    {
        _dbContext.Entry(current).CurrentValues.SetValues(newCase);
    }

    _dbContext.SaveChanges();

    return relatedCases;
}
0
Ogglas 26 Фев 2018 в 19:39