【问题标题】:Load navigation properties from 'flat' table (many to many)从“平面”表加载导航属性(多对多)
【发布时间】:2020-06-22 11:04:27
【问题描述】:

我必须与现有数据库集成,并使用 EF 实现数据加载(我们只读取,但从未使用此应用程序写入数据)。最后缺少的步骤是加载本地化数据。

模型如下所示:

UDC (UserDefinedCode) 表包含所有本地化值。每个其他实体都可以有 0-n 个代码,这些代码必须从 UDC 表中解析出来。

这是 Sequenz 上一个字段的 SQL 示例:

SELECT sequenz.Zulassungsstatus,
       udc.Beschreibung1
  FROM [dbo].[Sequenz] sequenz
  LEFT JOIN [dbo].[UserDefinedCode] udc ON udc.UserDefinedCode = 'MA_STATUS' AND udc.CodeValue = sequenz.Zulassungsstatus

相关实体:

public class SequenzEntity : TemporalEntity
{
    public int Zulassungsnummer { get; set; }

    public int Sequenznummer { get; set; }

    public string Zulassungsstatus { get; set; }

    public DateTime WiderrufVerzichtDatum { get; set; }

    public string SequenzName { get; set; }

    public string Zulassungsart { get; set; }

    public int BasisSequenzNummer { get; set; }

    public string Anwendungsgebiet { get; set; }

    public int ChargenblockadeAktiv { get; set; }

    public ApplikationsartEntity Applikationsart { get; set; }

    public PraeparatEntity Praeparat { get; set; }

    public IEnumerable<PackungEntity> Packungen { get; set; }

    public IEnumerable<DeklarationEntity> Deklarationen { get; set; }
}

public class UserDefinedCodeEntity : TemporalEntity
{
    public string SystemCode { get; set; }

    public string UserDefinedCode { get; set; }

    public string CodeValue { get; set; }

    public string SprachCode { get; set; }

    public string Beschreibung1 { get; set; }

    public string Beschreibung2 { get; set; }

    public string BeschreibungLang { get; set; }
}

EF模型正在DbContext.OnModelCreating中配置:

    protected override void OnModelCreating(ModelBuilder builder)
    {
        // Configure our entities
        builder.Entity<SequenzEntity>(ConfigureSequenzEntity);
        builder.Entity<UserDefinedCodeEntity>(ConfigureUserDefinedCodeEntity);
        [...]

        base.OnModelCreating(builder);
    }

    private static void ConfigureSequenzEntity(EntityTypeBuilder<SequenzEntity> builder)
    {
        ConfigureTemporalEntity(builder);

        builder.ToTable("Sequenz");

        builder.HasKey(sequenzEntity => new {sequenzEntity.Zulassungsnummer, sequenzEntity.Sequenznummer});

        builder.HasMany(sequenzEntity => sequenzEntity.Deklarationen)
               .WithOne(deklarationEntity => deklarationEntity.Sequenz);

        builder.HasMany(sequenzEntity => sequenzEntity.Packungen)
               .WithOne(packungEntity => packungEntity.Sequenz)
               .HasForeignKey(packungEntity => new {packungEntity.Zulassungsnummer, packungEntity.Sequenznummer});
    }

    private static void ConfigureUserDefinedCodeEntity(EntityTypeBuilder<UserDefinedCodeEntity> builder)
    {
        ConfigureTemporalEntity(builder);

        builder.ToTable("UserDefinedCode");

        builder.HasKey(userDefinedCodeEntity => new
                                                {
                                                    userDefinedCodeEntity.SystemCode,
                                                    userDefinedCodeEntity.UserDefinedCode,
                                                    userDefinedCodeEntity.CodeValue,
                                                    userDefinedCodeEntity.SprachCode
                                                });
    }

    private static void ConfigureTemporalEntity<TEntity>(EntityTypeBuilder<TEntity> builder)
        where TEntity : TemporalEntity
    {
        ConfigureBaseEntity(builder);

        builder.HasTemporalTable();

        builder.Property(baseEntity => baseEntity.VersionValidityStart)
               .HasColumnName("SysStartTime");

        builder.Property(baseEntity => baseEntity.VersionValidityEnd)
               .HasColumnName("SysEndTime");
    }

    private static void ConfigureBaseEntity<TEntity>(EntityTypeBuilder<TEntity> builder)
        where TEntity : BaseEntity
    {
        builder.HasKey(baseEntity => baseEntity.Id);
    }

我很难弄清楚如何设置模型和配置 EF 以加载导航属性。如果我添加

  • 序列实体:public IEnumerable&lt;UserDefinedCodeEntity&gt; ZulassungsstatusCodes { get; set; }
  • 用户定义代码实体:public IEnumerable&lt;SequenzEntity&gt; Sequenzen{ get; set; }

我显然得到了一个无法在 EF 中配置的多对多关系。

是否可以按照我的意愿使用 EF 加入 UDC 表?

【问题讨论】:

    标签: c# entity-framework-core ef-core-3.1 navigation-properties


    【解决方案1】:

    由于我找不到使用 EF 执行此操作的干净方法,因此我选择了另一种方法,其中实体本身不知道与语言相关的属性。现在是业务层的工作来聚合字段。

    创建一个包含所有必需属性的对象(投影):

        public override async Task<PackungDto> GetAsync(int id, DateTime? asOf = null)
        {
            var packungProjection = new PackungDetailProjection
                                    {
                                        SprachCode = CultureInfo.CurrentUICulture.GetOneLetterLanguage(),
                                        Packung = await _packungRepository.GetAsync(id, asOf, _includePropertiesGet),
                                        ZulassungsstatusCodes = await _codeRepository.GetCodesAsync(CodeType.Zulassungsstatus, asOf: asOf)
                                    };
    
            return Mapper.Map<PackungDto>(packungProjection);
        }
    

    并让 AutoMapper 使用此配置文件进行映射:

    internal sealed class PackungDetailProjectionToPackungDtoMappingProfile : Profile
    {
        #region Public Methods
    
        public PackungDetailProjectionToPackungDtoMappingProfile()
        {
            CreateMap<PackungDetailProjection, PackungDto>()
                // BaseEntity
                .ForMember(dest => dest.Id, exp => exp.MapFrom(src => src.Packung.Id))
    
                // Temporal Entity
                .ForMember(dest => dest.VersionValidityStart, exp => exp.MapFrom(src => src.Packung.VersionValidityStart))
                .ForMember(dest => dest.VersionValidityEnd, exp => exp.MapFrom(src => src.Packung.VersionValidityEnd))
    
                // Packung
                .ForMember(dest => dest.Gtin, exp => exp.MapFrom(src => src.Packung.Gtin))
                .ForMember(dest => dest.Zulassungsnummer, exp => exp.MapFrom(src => src.Packung.Zulassungsnummer))
                .ForMember(dest => dest.Sequenznummer, exp => exp.MapFrom(src => src.Packung.Sequenznummer))
                .ForMember(dest => dest.Packungscode, exp => exp.MapFrom(src => src.Packung.Packungscode))
                .ForMember(dest => dest.Name, exp => exp.MapFrom(src => $"{src.Packung.Sequenz.SequenzName.Trim()} {src.Packung.Packungsgroesse.Trim()}"))
    
                // Codes
                .ForMember(dest => dest.ZulassungsstatusDescription,
                           exp => exp.MapFrom(src => GetCodeDescription(src.ZulassungsstatusCodes.Where(entity => entity.CodeValue == src.Packung.Zulassungsstatus).ToList(),
                                                                        src.SprachCode,
                                                                        entity => entity.Beschreibung1)));
        }
    
        #endregion
    
        #region Private Methods
    
        /// <summary>
        ///     Gets the code description.
        /// </summary>
        /// <param name="codes">The codes.</param>
        /// <param name="sprachCode">The sprach code.</param>
        /// <param name="propertyValueSelector">
        ///     The selector specifying which property to use.
        ///     Usually <see cref="UserDefinedCodeEntity.Beschreibung1" />, <see cref="UserDefinedCodeEntity.Beschreibung2" /> or
        ///     <see cref="UserDefinedCodeEntity.BeschreibungLang" />
        /// </param>
        /// <returns>
        ///     The code description determined by following order:<br />
        ///     1. Using the user langauge.<br />
        ///     2. Using the default language.<br />
        ///     3. <c>Null</c>.
        /// </returns>
        private static string GetCodeDescription(ICollection<UserDefinedCodeEntity> codes, string sprachCode, Func<UserDefinedCodeEntity, string> propertyValueSelector)
        {
            var codeDescription = codes.FirstOrDefault(entity => entity.SprachCode == sprachCode);
    
            // Fall back to default langauge if the code could not be found for the user language 
            if (codeDescription == null && sprachCode != LanguageOneLetterConstants.DefaultCode)
            {
                codeDescription = codes.FirstOrDefault(entity => entity.SprachCode == LanguageOneLetterConstants.DefaultCode);
            }
    
            return codeDescription == null
                       ? null
                       : propertyValueSelector(codeDescription);
        }
    
        #endregion
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多