c# - EFCore - 键值从 C.Link 更改为 B.Link 在作为 A.Link 的公共抽象基类中实现
问题描述
为了使用 RSS 和 Atom 联合格式,对于在 Xamarin.Forms 应用程序中使用 .NET 依赖注入的应用程序来说是理想的,我设法实现了一个用于处理 RSS 和 Atom XML 的服务框架。我目前正在研究数据存储功能。
在代码库的摘录中,我实现了以下内容,用于将 RssChannel 和 RssItem 对象直接添加到数据库中。通过对 Rss 内容的数据库适配器类的静态方法的一些运行时反射,此代码最终在OnModelCreating(ModelBuilder builder)
builder.Entity<RssChannel>().HasKey(channel => channel.RssLink);
builder.Entity<RssChannel>().HasMany(channel => channel.Items).WithOne(item => item.Channel);
builder.Entity<RssItem>().HasKey(item => new { item.RssLink, item.ObjectDateString });
RssChannel
并且RssItem
在这里被实现为派生自抽象基类RssSyndicationObject
。
出于 XML 反序列化的目的,此类RssSyndicationObject
为属性提供了实现和 XML 注释支持RssLink
。然后,该属性及其 XML 注释将被继承到每个实现类,RssChannel
并且RssItem
.
RssItem
然后用属性 定义Channel
,这样就提供了指向RssChannel
发布. 的反向链接RssItem
。
在模型构建器中支持这么多的工作时,我在代码库的实现下遇到了以下消息。{RssItem.RssLink, RssItem.ObjectDateString}
它似乎表明正在修改基于集合的键值,以便RssItem.RssLink
随后将 替换RssChannel.RssLink
为RssChannel
每个人的 的值RssItem
。
Context 'SqliteStorageContext' started tracking 'RssChannel' entity with key '{RssLink: https://www.npr.org/templates/story/story.php?storyId=2}'.
dbug: Microsoft.EntityFrameworkCore.ChangeTracking[10806]
Context 'SqliteStorageContext' started tracking 'RssChannelImage' entity with key '{Uri: https://media.npr.org/images/podcasts/primary/npr_generic_image_300.jpg?s=200}'.
dbug: Microsoft.EntityFrameworkCore.ChangeTracking[10803]
The foreign key property 'RssItem.RssLink' was detected as changed from 'https://www.npr.org/2021/01/25/960354929/in-tiny-kansas-town-pandemic-skeptics-abound-amid-false-information-and-politics' to 'https://www.npr.org/templates/story/story.php?storyId=2' for entity with key '{RssLink: https://www.npr.org/templates/story/story.php?storyId=2, ObjectDateString: Mon, 25 Jan 2021 13:49:00 GMT}'.
dbug: Microsoft.EntityFrameworkCore.ChangeTracking[10806]
Context 'SqliteStorageContext' started tracking 'RssItem' entity with key '{RssLink: https://www.npr.org/templates/story/story.php?storyId=2, ObjectDateString: Mon, 25 Jan 2021 13:49:00 GMT}'.
dbug: Microsoft.EntityFrameworkCore.ChangeTracking[10803]
The foreign key property 'RssItem.RssLink' was detected as changed from 'https://www.npr.org/2021/01/25/960465917/israel-secures-covid-19-vaccine-doses-by-agreeing-to-share-medical-data-on-israe' to 'https://www.npr.org/templates/story/story.php?storyId=2' for entity with key '{RssLink: https://www.npr.org/templates/story/story.php?storyId=2, ObjectDateString: Mon, 25 Jan 2021 13:16:18 GMT}'.
dbug: Microsoft.EntityFrameworkCore.ChangeTracking[10806]
Context 'SqliteStorageContext' started tracking 'RssItem' entity with key '{RssLink: https://www.npr.org/templates/story/story.php?storyId=2, ObjectDateString: Mon, 25 Jan 2021 13:16:18 GMT}'.
dbug: Microsoft.EntityFrameworkCore.ChangeTracking[10803]
那么,这就变成了一个错误。
fail: TheNamespace.SyndicationDataManagerService[0]
Received exception for https://feeds.npr.org/2/rss.xml : OtherNamespace.ProviderFailureException: General error
---> System.InvalidOperationException: The instance of entity type 'RssItem' cannot be tracked because another instance with the key value '{RssLink: https://www.npr.org/templates/story/story.php?storyId=2, ObjectDateString: Mon, 25 Jan 2021 13:16:00 GMT}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.ThrowIdentityConflict(InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey key, InternalEntityEntry entry)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.NullableKeyIdentityMap`1.Add(InternalEntityEntry entry)
该密钥中使用的 URL 来自RssChannel.RssLink
,而不是原始RssItem.RssLink
。这一点,以及在运行时更改密钥的问题 - 有没有办法在不修改 和 之一或两者的实现的情况下解决这个RssChannel
问题RssItem
?
无论是什么可能导致“检测为已更改”消息中指示的更改,我都不相信它来自我自己的代码。
我希望能够将“RssLink”属性的共享实现保留在RssSyndicationObject
. 虽然我相信我已经找到了一个解决方法,在这些实现类中分别实现每个“链接”属性,但是必须有一种方法可以在模型构建器下使用流利的 API 来解决这个问题?
任何意见,将不胜感激。我查看了 EFCore 和传统实体框架实现的文档。我发现解决它的唯一方法——当直接在数据库中存储来自这些 XML 记录类的值时——是更改这些 XML 记录类的实现。
虽然我计划实现一个适配器框架,但在以后对此应用程序的一些测试中,我不相信我已经看到了这个问题的最后一个。
更新
使用以下作为RssChannel
- 而不是仅 的RssLink
的键RssChannel
,如上所述 - 现在似乎没有发生问题。至少它可能代表一种解决方法?
builder.Entity<RssChannel>().HasKey(channel => new { channel.RssLink, channel.RssTitle });
尽管如此,这可能无法解决问题发生的原因 - 例如一个问题,为什么RssItem
在运行时修改了键值?这样在密钥已经初始化之后,它将被更改为使用RssLink
项目的包含通道。查看代码,我无法想象RssItem
当仅将通道RssLink
用作 each 的单项键时,这将如何成为 any 键的结果RssChannel
。
类似基类的问题RssChannel
和RssItem
派生只是一个猜测。
至少,这里可能有一种猜测,作为一种解决方法,而不修改 XML 数据类的实现?
更多详细信息
代码库的摘录,在其目前的重构状态下。
public abstract class SyndicationObject
{
[XmlIgnore]
public string? ObjectTitle { get; set; }
[XmlIgnore]
public string? ObjectDateString { get; set; }
// ObjectDateString definition truncated.
// this property accesses a naïve ObjectDate property of type DateTime?
}
public abstract class RssSyndicationObject : SyndicationObject
{
[XmlElement("link", Namespace = RssConstants.NsUriRss)]
public string? RssLink { get; set; }
[XmlElement("title", Namespace = RssConstants.NsUriRss)]
public string? RssTitle { get => ObjectTitle; set => ObjectTitle = value; }
}
public class RssItem : RssSyndicationObject
{
protected RssChannel? channel;
[XmlIgnore]
public RssChannel? Channel { get => channel; set => channel = value; }
}
public class RssChannel : RssSyndicationObject
{
[XmlElement("item", Namespace = RssConstants.NsUriRss)]
public ItemsList<RssChannel, RssItem>? Items [...]
// Items property definition truncated for brevity.
// This property sets the containing RssChannel on each RssItem
// then stores the provided ItemsList<RssChannel,RssItem> under
// a protected field of the same type.
//
// Later item-> channel registration is provided in the
// ItemsList<T1,T2>.Add(...) implementation, such that may
// appear to be used under XML deserialization for this property
}
public class ItemsList<TChannel, TItem> : List<TItem>
where TChannel : RssChannel
where TItem : RssItem
{
private TChannel rssChannel;
public ItemsList(TChannel rssChannel) : base()
{
this.rssChannel = rssChannel;
}
new public void Add(TItem item)
{
base.Add(item);
item.Channel = rssChannel;
}
}
尽管与此问题无关,但我一直在使用 RssItem[]?通道项目集的存储类型。就 XML 反序列化而言,这成功了。但是,数据库适配器无法处理此属性。虽然ItemsList<..>
实现(在此处介绍的修订版中)可能很幼稚,但它不提供排序或其他功能,但它至少在数据存储测试期间发挥了作用。
在对这个问题的一些早期重构中,在尝试重新设计RssItem
和RssChannel
链接属性时,当我定义 egstring? RssItem.ItemLink
和string? RssChannel.ChannelLink
属性每个都存储在类的本地字段中时,这个问题已经停止发生,而不是使用RssLink
来自的公共属性RssSyndicationObject
,在这两个实现类中。
解决方案
推荐阅读
- angular - Reference.push 失败:第一个参数在属性“Questions.code”中包含未定义
- python - TypeError:get() 为参数“task_id”获取了多个值
- arrays - 我正在尝试做一个自定义 UITableViewCell 但我无法在桌子上显示结果
- reactjs - 如何为 textarea 街道地址启用自动填充?
- html - 电子 > 这是一个错误吗?
- javascript - React 如何在组件视图中打印 PDF 文件
- azure - Azure DevOps (On Premise) - Minimatch Pattern 下载工件的多个部分
- c# - 从字符串中获取特定部分
- c++ - 随着时间的推移确定分配调用站点(代码行)
- javascript - 寻求帮助以从 .filter() 获得正确结果