Sql server 实体框架:从字典创建模型<;TKey,TValue>;要映射到数据库表,请执行以下操作:

Sql server 实体框架:从字典创建模型<;TKey,TValue>;要映射到数据库表,请执行以下操作:,sql-server,entity-framework,ef-code-first,Sql Server,Entity Framework,Ef Code First,早些时候,我有一个名为ApplicationConfiguration的表,它只有[Key]、[Value]列来存储一些配置数据。这是使用SQL查询直接查询的 现在我打算使用实体框架(EF)代码优先的方法来查询这个表。该表的特点是,该表在其生存期内只有固定数量的行。只能更新“值”列 因此,按照代码优先的方法,我们必须首先编写POCO类及其属性,这些属性将映射到基础表中的列。然而,我希望有一个字典结构来表示这些配置对。我关心的是,EF是否能够针对特定对的值的任何更新触发更新查询 另外,由于我使用的

早些时候,我有一个名为ApplicationConfiguration的表,它只有[Key]、[Value]列来存储一些配置数据。这是使用SQL查询直接查询的

现在我打算使用实体框架(EF)代码优先的方法来查询这个表。该表的特点是,该表在其生存期内只有固定数量的行。只能更新“值”列

因此,按照代码优先的方法,我们必须首先编写POCO类及其属性,这些属性将映射到基础表中的列。然而,我希望有一个字典结构来表示这些配置对。我关心的是,EF是否能够针对特定对的值的任何更新触发更新查询

另外,由于我使用的是代码优先的方法,所以我希望在第一次执行应用程序时动态创建表本身之后,向表添加一些种子数据(即固定数量的行及其初始内容)


如果无法使用词典,请建议其他选择。提前谢谢。

我不认为你能把一张表直接映射到字典;您可能需要编写自己的包装器来从表中填充字典并将其更新回数据库。每个实体都是给定表中的一行。。。类似这样(未经测试):

公共字典GetDictionary() { Dictionary dic=新字典(); 使用(var db=new Context()) { var configs=db.ApplicationConfiguration.Select(); foreach(配置中的var条目) { 添加(config.Key,config.Value); } } 返回dic; } 公共void SaveConfig(字典dic) { 使用(var db=new Context()) { foreach(dic中的KeyValuePair kvp) { if(!db.ApplicationConfiguration.First(a=>a.Key==kvp.Key).Value==kvp.Value) { var ac=新的应用程序配置(); ac.键=kvp.键; 交流值=千伏值; db.Entry(ac.State=EntityState.Modified; } } db.SaveChanges(); } } 对于第二个问题,您希望使用
Seed()
方法向数据库添加初始值。有关示例实现,请参见。

以这种方式编码:

public class ApplicationConfiguration
{
    public int Id { get; set; }
    public string Key { get; set; }
    public int Value { get; set; } // should be string, but I'm lazy
}

class Context : DbContext
{
    internal class ContextInitializer : DropCreateDatabaseIfModelChanges<Context>
    {
        protected override void Seed(Context context)
        {
            var defaults = new List<ApplicationConfiguration>
            {
                new ApplicationConfiguration {Key = "Top", Value = 5},
                new ApplicationConfiguration {Key = "Bottom", Value = 7},
                new ApplicationConfiguration {Key = "Left", Value = 1},
                new ApplicationConfiguration {Key = "Right", Value = 3}
            };

//            foreach (var c in defaults)
//                context.ConfigurationMap.Add(c.Key, c); // by design, no IReadOnlyDictionary.Add

            foreach (var c in defaults)
                context.ApplicationConfigurations.Add(c);

            base.Seed(context);
        }
    }

    public Context()
    {
        Database.SetInitializer(new ContextInitializer());
    }

    private IDbSet<ApplicationConfiguration> ApplicationConfigurations
    {
        get { return Set<ApplicationConfiguration>(); }
    }

    public IReadOnlyDictionary<string, ApplicationConfiguration> ConfigurationMap
    {
        get { return ApplicationConfigurations.ToDictionary(kvp => kvp.Key, kvp => kvp); }
    }
}
using (var context = new Context())
{
    ReadConfigurationOnly(context.ConfigurationMap);
}

using (var context = new Context())
{
    ModifyConfiguration(context.ConfigurationMap);
    context.SaveChanges();
}

static void ReadConfigurationOnly(IReadOnlyDictionary<string, ApplicationConfiguration> configuration)
{
    foreach (var k in configuration.Keys)
        Console.WriteLine("{0} = {1}", k, configuration[k].Value);
}

static void ModifyConfiguration(IReadOnlyDictionary<string, ApplicationConfiguration> configuration)
{
    foreach (var k in configuration.Keys)
        configuration[k].Value++; // this is why I was lazy, using an int for a string
}
公共类应用程序配置
{
公共int Id{get;set;}
公共字符串密钥{get;set;}
公共int值{get;set;}//应该是字符串,但我很懒
}
类上下文:DbContext
{
内部类ContextInitializer:DropCreateDatabaseIfModelChanges
{
受保护的覆盖无效种子(上下文)
{
var defaults=新列表
{
新应用程序配置{Key=“Top”,Value=5},
新应用程序配置{Key=“Bottom”,Value=7},
新应用程序配置{Key=“Left”,值=1},
新应用程序配置{Key=“Right”,Value=3}
};
//foreach(默认情况下为var c)
//context.ConfigurationMap.Add(c.Key,c);//根据设计,没有IReadOnlyDictionary.Add
foreach(默认情况下为var c)
context.ApplicationConfigurations.Add(c);
种子(上下文);
}
}
公共上下文()
{
SetInitializer(新的ContextInitializer());
}
专用IDbSet应用程序配置
{
获取{return Set();}
}
公共IReadOnlyDictionary配置图
{
获取{return ApplicationConfigurations.ToDictionary(kvp=>kvp.Key,kvp=>kvp);}
}
}
使用这种方式:

public class ApplicationConfiguration
{
    public int Id { get; set; }
    public string Key { get; set; }
    public int Value { get; set; } // should be string, but I'm lazy
}

class Context : DbContext
{
    internal class ContextInitializer : DropCreateDatabaseIfModelChanges<Context>
    {
        protected override void Seed(Context context)
        {
            var defaults = new List<ApplicationConfiguration>
            {
                new ApplicationConfiguration {Key = "Top", Value = 5},
                new ApplicationConfiguration {Key = "Bottom", Value = 7},
                new ApplicationConfiguration {Key = "Left", Value = 1},
                new ApplicationConfiguration {Key = "Right", Value = 3}
            };

//            foreach (var c in defaults)
//                context.ConfigurationMap.Add(c.Key, c); // by design, no IReadOnlyDictionary.Add

            foreach (var c in defaults)
                context.ApplicationConfigurations.Add(c);

            base.Seed(context);
        }
    }

    public Context()
    {
        Database.SetInitializer(new ContextInitializer());
    }

    private IDbSet<ApplicationConfiguration> ApplicationConfigurations
    {
        get { return Set<ApplicationConfiguration>(); }
    }

    public IReadOnlyDictionary<string, ApplicationConfiguration> ConfigurationMap
    {
        get { return ApplicationConfigurations.ToDictionary(kvp => kvp.Key, kvp => kvp); }
    }
}
using (var context = new Context())
{
    ReadConfigurationOnly(context.ConfigurationMap);
}

using (var context = new Context())
{
    ModifyConfiguration(context.ConfigurationMap);
    context.SaveChanges();
}

static void ReadConfigurationOnly(IReadOnlyDictionary<string, ApplicationConfiguration> configuration)
{
    foreach (var k in configuration.Keys)
        Console.WriteLine("{0} = {1}", k, configuration[k].Value);
}

static void ModifyConfiguration(IReadOnlyDictionary<string, ApplicationConfiguration> configuration)
{
    foreach (var k in configuration.Keys)
        configuration[k].Value++; // this is why I was lazy, using an int for a string
}
使用(var context=new context())
{
只读配置(context.ConfigurationMap);
}
使用(var context=new context())
{
修改配置(context.ConfigurationMap);
SaveChanges();
}
静态无效只读配置(IREADONLYDICTIONAL配置)
{
foreach(configuration.Keys中的var k)
WriteLine(“{0}={1}”,k,配置[k].Value);
}
静态无效修改配置(IREADONLYDICTIONAL配置)
{
foreach(configuration.Keys中的var k)
配置[k].Value++;//这就是我懒惰的原因,用int表示字符串
}
所以,我是这样写的-使用
int
Value
属性而不是
字符串
-这样我就可以一遍又一遍地运行“Used this way”代码,并且每次都可以看到数据库的更新,而不必想出其他方法以有趣的方式更改

在这里使用
IReadOnlyDictionary
而不是
IReadOnlyDictionary
并不是很好,这是我们真正喜欢的方式,但我们可以很容易地修改集合值,而不必求助于使用字典作为输入的笨拙的
Set
方法。当然,缺点是我们必须满足于
configuration[key].Value=“new Value”
而不是
configuration[key]=“new Value”
,但正如我所说,我认为这是值得的

编辑

该死!我专门写了这段代码来回答这个问题,但我想我非常喜欢它,我将把它添加到我的技巧包中。。。当我的公司从本地数据库转到云中的Azure实例,并且当前的app.config必须进入数据库时,这将非常适合


现在我只需要一个
ContextInitializer
System.Configuration.ConfigurationManager
作为一个ctor参数,以便从现有的app.config中创建一个新的数据库…

漂亮的折痕:
应用程序配置
可以私有化,并且一切仍然有效,使Dictionary属性成为访问数据的唯一方法。我正在更新答案以反映这一点。