简体   繁体   中英

Entity framework serialization multiple properties to one column

I would like to map the following class to a table, using entity framework, preferably with fluent api.

public class MyEntity
{
    public int Id {get;set;}
    public string Name {get;set;}
    public int Age {get;set;}
    public string OtherData {get;set;}
    public List<Blabla> BlaBlaList {get;set;}
}

Table MyEntity:

column Id
column Name
column SerializedData

Is it possible map only Id and Name to columns, and all other properties serialized in the "SerializedData" column?

If not "only" the other properties, the whole object may also be serialized in the SerializedData column

Thanks, Steven

Another answer similar to Drew's answer would be to do something like:

public class MyEntity : IMySerializable
{
  public int Id {get;set;}
  public string Name {get;set;}

  [NotMapped]
  public int Age {get;set;}
  [NotMapped]
  public string OtherData {get;set;}
  [NotMapped]
  public List<Blabla> BlaBlaList {get;set;}

  public byte[] SerializedData  
  {
    get
    {
      return this.MySerialize();
    } 
    set 
    {
      this.MyDeserialize(value);
    }
  }
}

Then an extension method to allow you to do this to more than one entity:

public static IMySerializableExtensions
{
  public static byte[] MySerialize<T>(this T instance)
    where T : IMySerializable
  {
    byte[] result = // ...

    // code

    return result;
  }

  public static void MyDeserialize<T>(this T instance, byte[] value)
    where T : IMySerializable
  {
     // deserialize value and update values
  }
}

You can figure out what properties to deserialize/serialize because they will have the NotMappedAttribute on them.

You'll have to do it yourself...

I'd recommend creating a separate class for your database mapping and leaving 'MyEntity' as a POCO. It's up to preference in the end, but I prefer to keep my entities as close as possible to the database structure. It's easier to maintain that way.

So, having said that, create a separate class which is the object you'll actually interact with, and give it an instance method to serialize itself, and a static method for deserialization. I've used JSON here but you can do whatever you want. Note I also added a method to convert it to a MyDBEntity: your logic will likely be somewhere else but this should give you an idea of how to do it.

public class MyEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string OtherData { get; set; }
    public List<int> BlaBlaList { get; set; }

    public byte[] Serialize()
    {
        string json = JsonConvert.SerializeObject(this);
        return Encoding.ASCII.GetBytes(json);
    }

    public static string Deserialize(byte[] objectBytes)
    {
        return Encoding.ASCII.GetString(objectBytes);
    }

    public MyDBEntity ConvertToDBEntity()
    {
        MyDBEntity dbEntity = new MyDBEntity();
        dbEntity.ID = Id;
        dbEntity.Name = Name;
        dbEntity.SerializedData = this.Serialize();
        return dbEntity;
    }
}

public class MyDBEntity
{
    public int ID { get; set; }
    public string Name { get; set; }
    public byte[] SerializedData { get; set; }
}

Next, add the MyDBEntity class to your context:

public class EFContext : DbContext
{
    public DbSet<MyDBEntity> Entities { get; set; }
}

That's about it! now you can do stuff like

 using (var db = new EFContext())
    {
        MyEntity me = new MyEntity();
        me.Name = "Bob";
        me.Age = 25;
        me.OtherData = "he does stuff";
        me.BlaBlaList = new List<int> { 7, 8, 9 };
        MyDBEntity newEntity = me.ConvertToDBEntity();

        db.Entities.Add(newEntity);
        db.SaveChanges();
    }

I worked up a little console app for this answer, I put it on Github if you like.

Although this question was asked for Entity Framework, I had the exact same question but for Entity Framework Core. As it turns out, EF Core has an even more elegant approach to storing serialized data in a single column while allowing individual properties to exist on the domain mode. More elegant, because it does not require any changes on the domain model itself.

It goes like this:

Your entity remains as-is:

public class MyEntity
{
    public int Id {get;set;}
    public string Name {get;set;}
    public int Age {get;set;}
    public string OtherData {get;set;}
    public List<Blabla> BlaBlaList {get;set;}
}

Then configure your model to ignore the properties you do not want to map to individual columns, and add a shadow property instead:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Ignore(x => x.Age);
    modelBuilder.Ignore(x => x.OtherData);
    modelBuilder.Ignore(x => x.BlaBlaList);

    // Adding a "shadow" property called "Data".
    modelBuilder.Property<string>("Data");
}

When you now save your entity, you can simply serialize the properties you want into a JSON string and set the "Data" shadow property, like this:

var entity = new MyEntity { ... };

var data = new
{
   entity.Age,
   entity.OtherData,
   entity.BlaBlaList
};

var json = JsonConvert.Serialize(data);

_dbContext.Property("Data").CurrentValue = json;

And when loading your entity from storage, make sure to rehydrate the properties:

var entity = await _dbContext.MyEntities.FirstOrDefaultAsync(...);

// Simply re-constructing the anonymous type for deserialization. It's not necessary to actually initialize each field with the current values (which are empty anyway), but this is just a convenient way to model the anonymous type.

var data = new
{
   entity.Age,
   entity.OtherData,
   entity.BlaBlaList
};

var json = _dbContext.Property("Data").CurrentValue = json;
data = JsonConvert.DeserializeAnonymousType(json, data);

entity.Age = data.Age;
entity.OtherData = data.OtherData;
entity.BlaBlaList = data.BlaBlaList;

And that's it. EF Core allows you to use your domain models as pure, clean POCOs by leveraging their new shadow properties feature.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM