[英]Map a Dictionary in Entity Framework Code First Approach
我有一个这样的字典:
/// <summary>
/// Gets the leave entitlement details.
/// </summary>
/// <value>The leave entitlement details.</value>
public Dictionary<string, EmployeeLeaveEntitlement> LeaveEntitlementDetails { get; set; }
我想将它映射到数据库。 是否可以使用受保护或私有List <>? 如:
/// <summary>
/// Gets the leave entitlement details.
/// </summary>
/// <value>The leave entitlement details.</value>
public Dictionary<string, EmployeeLeaveEntitlement> LeaveEntitlementDetails { get; set; }
public List<EmployeeLeaveEntitlement> LeveEntitlementStore
{
get
{
List<EmployeeLeaveEntitlement> leaveEntitlements = new List<EmployeeLeaveEntitlement>();
foreach (KeyValuePair<string, EmployeeLeaveEntitlement> leaveType in LeaveEntitlementDetails)
{
leaveEntitlements.Add(leaveType.Value);
}
return leaveEntitlements;
}
set
{
foreach (EmployeeLeaveEntitlement item in value)
{
this.LeaveEntitlementDetails.Add(item.LeaveType, item);
}
}
}
谁能帮我?
实体框架目前不支持本地映射字典。
有关更多信息和解决方法,请参阅以下内容:
EF Code First - 将字典或自定义类型映射为nvarchar
http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/a51ba903-2b8b-448e-8677-d140a0b43e89/
所以今天我遇到了同样的问题,在考虑之后我找到了一个很酷的解决方案,即使我迟到了,也想与社区分享。 基本上我已经创建了一个包装系统,它将Dictionary
的数据作为XML Column
保存到Database
中,所以稍后我也可以根据需要从数据库中查询XML。
首先,这是我所有模特的骨头:
public abstract class BaseEntity
{
/// <summary>
/// ID of the model
/// </summary>
public int ID { get; set; }
}
假设我有一个包含Dictionary<string,string>
和一个String
属性,该属性包含在XML
序列化和反序列化字典的逻辑,如下面的代码片段所示:
public class MyCoolModel : Base.BaseEntity
{
/// <summary>
/// Contains XML data of the attributes
/// </summary>
public string AttributesData
{
get
{
var xElem = new XElement(
"items",
Attributes.Select(x => new XElement("item", new XAttribute("key", x.Key), new XAttribute("value", x.Value)))
);
return xElem.ToString();
}
set
{
var xElem = XElement.Parse(value);
var dict = xElem.Descendants("item")
.ToDictionary(
x => (string)x.Attribute("key"),
x => (string)x.Attribute("value"));
Attributes = dict;
}
}
//Some other stuff
/// <summary>
/// Some cool description
/// </summary>
[NotMapped]
public Dictionary<string, string> Attributes { get; set; }
}
然后我实现了一个BaseMapping
类,它从EntityTypeConfiguration<T>
继承
class BaseMapping<TEntity> : EntityTypeConfiguration<TEntity>
where TEntity : Model.Base.BaseEntity
{
public BaseMapping()
{
//Some basic mapping logic which I want to implement to all my models
}
}
并在MyCoolModel
的自定义Mapping
MyCoolModel
class MyCoolModelMapping
: BaseMapping<Model.MyCoolModel>
{
public MyCoolModelMapping()
{
Property(r => r.AttributesData).HasColumnType("xml");
}
}
现在请注意,当EntityFramework
请求AttributesData
值时,它只是序列化字典,当我从数据库中检索数据时,同样的情况发生,并且EntityFramework将数据设置为字段,然后反序列化对象并将其设置为字典。
最后我不得不override
的OnModelCreating
我的DbContext的
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new Mappings.BaseMapping<SomeOtherModel>());
modelBuilder.Configurations.Add(new Mappings.MyCoolModelMapping());
//Other logic
}
就是这样! 现在我可以使用业务逻辑中的字典,这个“包装”处理将数据保存到DB
并从中检索数据所需的所有内容。
EF Core 2.1引入了一项称为价值转换的新功能:
值转换器允许在读取或写入数据库时转换属性值。
此功能大大简化了先前答案中提到的序列化方法,这意味着,引入额外的“帮助器”属性并将字典属性标记为[NotMapped]
变得不必要。
以下是为您的案例量身定制的一些代码行(注意,我使用的是Json.NET ,但您可以随意使用您选择的序列化程序):
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace My.Name.Space
{
public class MyEntity
{
public int Id { get; set; }
public Dictionary<string, EmployeeLeaveEntitlement> LeaveEntitlementDetails { get; set; }
}
public class MyEntityConfiguration : IEntityTypeConfiguration<MyEntity>
{
public void Configure(EntityTypeBuilder<MyEntity> builder)
{
builder.ToTable("MyEntity");
builder.HasKey(e => e.Id);
builder
.Property(e => e.LeaveEntitlementDetails)
.IsRequired()
.HasConversion(
v => JsonConvert.SerializeObject(v),
v => v == null
? new Dictionary<string, EmployeeLeaveEntitlement>() // fallback
: JsonConvert.DeserializeObject<Dictionary<string, EmployeeLeaveEntitlement>>(v)
);
}
}
}
我有一个与EF类似的问题,我想将一个查询返回列表转换为类属性的字典等价物。 非常类似于你想如何让LeaveEntitlementDetails
包装LeveEntitlementStore
例如:
class A
{
[NotMapped()]
public Dictionary<int, DataType> Data {get; set}
//refers to Data.Values
public ICollection<DataType> DataAsList {get; set}
}
我希望DataAsList
基本上包装Data.Values
经过大量的反复试验,我发现EF,对于集合(可能更多)来说,通过getter的返回值(而不是setter)来改变。 即从我的db初始化时:
var pollquery=From bb In DBM.Dbi.DataTable.Includes("DataAsList")
Where bb.Id = id
Select bb;
ClassA objInstance = pollquery.First();
从来没有调用过ClassA.DataAsList
的setter,但是在我的对象内部构造期间getter ....结论:EF使用从属性ClassA.DataAsList
的getter中检索的引用,并向其添加对象。
所以我在ObservableCollection中包含了我的getter的DataAsList
返回值,并为CollectionChanged args添加了一个处理程序,果然,我的CollectionChanged处理程序正在拾取.Add
调用。
所以继承我的hackaround -aroundaround:
class A : INotifyPropertyChanged
{
//So we can let EF know a complex property has changed
public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
//here's our actual data, rather than an auto property, we use an explicit member definition so we can call PropertyChanged when Data is changed
private Dictionary<int, DataType> m_data = new Dictionary<int, DataType>();
//not mapped property as it's not mapped to a column in EF DB
[NotMapped()]
public Dictionary<int, DataType> Data {
get { return m_data; }
set {
m_data = value;
//now call PropertyChanged for our Front (so EF will know it's been changed)
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs("DataAsList"));
}
}
}
//this is our front for the data, that we use in EF to map data to
[DebuggerHidden()]
public ICollection<DataType> DataAsList {
get {
ObservableCollection<DataType> ob = new ObservableCollection<DataType>(Data.Values());
ob.CollectionChanged += Handles_entryListChanged;
return ob;
}
set {
//clear any existing data, as EF is trying to set the collections value
Data.Clear();
//this is how, in my circumstance, i converted my object into the dictionary from an internal obj.Id property'
foreach (DataType entry in value) {
entryions.Add(entry.id, entry);
}
}
}
//This will now catch wind of any changes EF tries to make to our DataAsList property
public void Handles_entryListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//Debugger.Break()
switch (e.Action) {
case NotifyCollectionChangedAction.Add:
foreach (DataType entry in e.NewItems) {
m_data.Add(entry.Id, entry);
}
break;
default:
Debugger.Break();
break;
}
}
}
注意魔术是:
public ICollection<DataType> DataAsList {
get {
ObservableCollection<DataType> ob = new ObservableCollection<DataType>(Data.Values());
ob.CollectionChanged += Handles_entryListChanged;
return ob;
}
我们订阅对返回列表所做的任何更改,以及我们处理的Handles_entryListChanged
,并基本上复制所做的任何更改。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.