code

Entity Framework-코드 우선-목록을 저장할 수 없음

codestyles 2020. 10. 6. 08:14
반응형

Entity Framework-코드 우선-목록을 저장할 수 없음


나는 그런 수업을 썼다.

class Test
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    [Required]
    public List<String> Strings { get; set; }

    public Test()
    {
        Strings = new List<string>
        {
            "test",
            "test2",
            "test3",
            "test4"
        };
    }
}

internal class DataContext : DbContext
{
    public DbSet<Test> Tests { get; set; }
}

코드 실행 후 :

var db = new DataContext();
db.Tests.Add(new Test());
db.SaveChanges();

내 데이터는 저장되지만 Id. 문자열 목록에 적용되는 테이블이나 관계가 없습니다 .

내가 도대체 ​​뭘 잘못하고있는 겁니까? 나는 또한 Strings 를 만들려고 시도했지만 virtual아무것도 변경하지 않았습니다.

도와 주셔서 감사합니다.


Entity Framework는 기본 형식 컬렉션을 지원하지 않습니다. 엔티티 (다른 테이블에 저장 됨)를 생성하거나 일부 문자열 처리를 수행하여 목록을 문자열로 저장하고 엔티티가 구체화 된 후 목록을 채울 수 있습니다.


나는 이것이 오래된 질문이라는 것을 알고 있으며 Pawel이 정답을 주었으므로 문자열 처리를 수행하는 방법에 대한 코드 예제를 보여주고 기본 유형 목록에 대한 추가 클래스를 피하고 싶었습니다.

public class Test
{
    public Test()
    {
        _strings = new List<string>
        {
            "test",
            "test2",
            "test3",
            "test4"
        };
    }

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    private List<String> _strings { get; set; }

    public List<string> Strings
    {
        get { return _strings; }
        set { _strings = value; }
    }

    [Required]
    public string StringsAsString
    {
        get { return String.Join(',', _strings); }
        set { _strings = value.Split(',').ToList(); }
    }
}

EF Core 2.1 이상 :

특성:

public string[] Strings { get; set; }

OnModelCreating :

modelBuilder.Entity<YourEntity>()
            .Property(e => e.Strings)
            .HasConversion(
                v => string.Join(',', v),
                v => v.Split(',', StringSplitOptions.RemoveEmptyEntries));

구조에 JSON.NET .

JSON으로 직렬화하여 데이터베이스에 유지하고 역 직렬화하여 .NET 컬렉션을 재구성합니다. 이것은 Entity Framework 6 및 SQLite에서 예상했던 것보다 더 잘 수행되는 것 같습니다. 나는 당신이 요청한 것을 알고 List<string>있지만 여기에 잘 작동하는 훨씬 더 복잡한 컬렉션의 예가 있습니다.

지속 형 속성에 태그를 지정 [Obsolete]했으므로 정상적인 코딩 과정에서 "이것은 당신이 찾고있는 속성이 아닙니다"라는 것이 매우 분명 할 것입니다. "real"속성에는 태그가 지정 [NotMapped]되므로 Entity 프레임 워크는이를 무시합니다.

(관련되지 않은 탄젠트) : 더 복잡한 유형으로 동일한 작업을 수행 할 수 있지만 해당 객체의 속성을 쿼리하기가 너무 어렵게 만들었습니까? (예, 제 경우에는).

using Newtonsoft.Json;
....
[NotMapped]
public Dictionary<string, string> MetaData { get; set; } = new Dictionary<string, string>();

/// <summary> <see cref="MetaData"/> for database persistence. </summary>
[Obsolete("Only for Persistence by EntityFramework")]
public string MetaDataJsonForDb
{
    get
    {
        return MetaData == null || !MetaData.Any()
                   ? null
                   : JsonConvert.SerializeObject(MetaData);
    }

    set
    {
        if (string.IsNullOrWhiteSpace(value))
           MetaData.Clear();
        else
           MetaData = JsonConvert.DeserializeObject<Dictionary<string, string>>(value);
    }
}

이 답변은 @Sasan@CAD bloke가 제공 한 답변을 기반으로합니다 .

EF Core 2.1 이상에서만 작동 (.NET Standard와 호환되지 않음) (Newtonsoft JsonConvert)

builder.Entity<YourEntity>().Property(p => p.Strings)
    .HasConversion(
        v => JsonConvert.SerializeObject(v),
        v => JsonConvert.DeserializeObject<List<string>>(v));

EF Core 유창한 구성을 사용하여 ListJSON으로 /에서 직렬화 / 역 직렬화합니다 .

이 코드가 노력할 수있는 모든 것의 완벽한 조합 인 이유 :

  • Sasn의 원래 답변의 문제는 목록의 문자열에 쉼표 (또는 구분 기호로 선택한 문자)가 포함되어 있으면 단일 항목을 여러 항목으로 바꾸지 만 읽기가 가장 쉽고 읽기 쉽고 가장 간결합니다.
  • CAD 블로크의 답변의 문제점은보기 흉하고 모델을 변경해야한다는 것입니다. 이는 잘못된 설계 관행입니다 ( Sasan의 답변 에 대한 Marcell Toth의 의견 참조 ). 그러나 데이터에 안전한 유일한 대답입니다.

단순화하기 위해-

엔티티 프레임 워크는 프리미티브를 지원하지 않습니다. 래핑 할 클래스를 만들거나 다른 속성을 추가하여 목록을 문자열로 형식화합니다.

public ICollection<string> List { get; set; }
public string ListString
{
    get { return string.Join(",", List); }
    set { List = value.Split(',').ToList(); }
}

물론 Pawel은 정답을 제시했습니다 . 그러나이 게시물 에서 EF 6+ 이후 개인 속성을 저장할 수 있음을 발견했습니다 . 따라서 잘못된 방법으로 문자열을 저장할 수 없기 때문에이 코드를 선호합니다.

public class Test
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Column]
    [Required]
    private String StringsAsStrings { get; set; }

    public List<String> Strings
    {
        get { return StringsAsStrings.Split(',').ToList(); }
        set
        {
            StringsAsStrings = String.Join(",", value);
        }
    }
    public Test()
    {
        Strings = new List<string>
        {
            "test",
            "test2",
            "test3",
            "test4"
        };
    }
}

ScalarCollection배열을 제한하고 몇 가지 조작 옵션 ( Gist )을 제공하는 컨테이너를 사용할 수 있습니다 .

용법:

public class Person
{
    public int Id { get; set; }
    //will be stored in database as single string.
    public SaclarStringCollection Phones { get; set; } = new ScalarStringCollection();
}

암호:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;

namespace System.Collections.Specialized
{
#if NET462
  [ComplexType]
#endif
  public abstract class ScalarCollectionBase<T> :
#if NET462
    Collection<T>,
#else
    ObservableCollection<T>
#endif
  {
    public virtual string Separator { get; } = "\n";
    public virtual string ReplacementChar { get; } = " ";
    public ScalarCollectionBase(params T[] values)
    {
      if (values != null)
        foreach (var item in Items)
          Items.Add(item);
    }

#if NET462
    [Browsable(false)]
#endif
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Obsolete("Not to be used directly by user, use Items property instead.")]
    public string Data
    {
      get
      {
        var data = Items.Select(item => Serialize(item)
          .Replace(Separator, ReplacementChar.ToString()));
        return string.Join(Separator, data.Where(s => s?.Length > 0));
      }
      set
      {
        Items.Clear();
        if (string.IsNullOrWhiteSpace(value))
          return;

        foreach (var item in value
            .Split(new[] { Separator }, 
              StringSplitOptions.RemoveEmptyEntries).Select(item => Deserialize(item)))
          Items.Add(item);
      }
    }

    public void AddRange(params T[] items)
    {
      if (items != null)
        foreach (var item in items)
          Add(item);
    }

    protected abstract string Serialize(T item);
    protected abstract T Deserialize(string item);
  }

  public class ScalarStringCollection : ScalarCollectionBase<string>
  {
    protected override string Deserialize(string item) => item;
    protected override string Serialize(string item) => item;
  }

  public class ScalarCollection<T> : ScalarCollectionBase<T>
    where T : IConvertible
  {
    protected override T Deserialize(string item) =>
      (T)Convert.ChangeType(item, typeof(T));
    protected override string Serialize(T item) => Convert.ToString(item);
  }
}

참고 URL : https://stackoverflow.com/questions/20711986/entity-framework-code-first-cant-store-liststring

반응형