【问题标题】:Foreign Key in Entity Framework in C#C# 实体框架中的外键
【发布时间】:2016-06-03 06:25:58
【问题描述】:

我已经使用 java 和 hibernate 多年,现在我开始使用 C#,所以我想做的事情或多或少像我在 java 中所做的那样。

我喜欢注释,因为我喜欢将所有配置与实体保持在一起。

我有两个班级,城市和国家,城市有国家的外键。

public class City {
    [Key]
    public long id { get; set; }
    public long countryId { get; set; }
    [ForeignKey("countryId")]
    public virtual Country country { get; set; }
    public string city { get; set; }
}

public class Country {
    [Key]
    public long id { get; set; }
    public string country { get; set; }
    public ICollection<City> Cities { get; set; }
}

我不知道是否真的需要与国家相关的城市中的属性(countryIdcountry),以及是否有办法只引用类

public Country country { get; set; }

另一个问题是当我创建一个新城市时

public class CityService:ICityService {
    public City getCity(string cityTxt, Country country) {
        City city = null;
        using (var ctx = new Context()) {
            city = ctx.Cities.Where(it => it.city == cityTxt && it.country.id == country.id).FirstOrDefault();
            if (city == null) {
                city = ctx.Cities.Add(new City { city = cityTxt, countryId = country.id });
                ctx.SaveChanges();
            }
        }
        return city;
    }
}

我设置了countryId,保存新城市后,city.country为空,有没有办法填充city.country属性?如果我设置 countryIdcountry 属性

city = ctx.Cities.Add(new City { city = cityTxt, countryId = country.id , country = country });

ctx.SaveChanges() 之后,会在数据库中创建一个重复的国家/地区实例。

我仍在将我的 java 概念迁移到 C#,因此非常感谢任何好的教程参考(如果使用 Fluent API 的注释更好)。

提前致谢。

【问题讨论】:

  • C# 中的属性应该使用 TitleCase。
  • @Gusdor 在 C# 中没有技术上的原因 - 但是,微软的指导方针很明确,应该这样做,每个人都这样做。我认识的每家公司都有接近微软的指导方针,它们都符合 CamelCase 属性命名
  • @Mafii 我想你的意思是Title(或Pascal)案例。虽然CamelCase 确实可以指代这种大小写(通常称为UpperCamelCase),但术语CamelCase 通常指的是“第一个字母小写,其余单词大写”(likeThis)。 Even Microsoft refers to CamelCase as the lowercase one :-)
  • @Jcl 哎呀,但我希望它现在清楚我的意思
  • @KasparsOzols 那是你的意见,我同意。有些人可能不同意...

标签: c# entity-framework


【解决方案1】:

拥有外键属性并不是严格要求的。不过有一个很好(正如 @GertArnold 在 cmets 中指出的那样,this answer gives more information about this)。

要加载country 属性,您需要实际加载它。这可以通过多种方式完成:

  1. 当您查询它时,使用 Include 预先加载它:

    myContext.Cities.Include(x => x.country).Where(...);
    
  2. 如果您的上下文启用了代理和延迟加载(默认情况下),您可以访问该属性以进行读取(当上下文未释放时)以加载它:

    using(var myContext = new Context())
    {
      var city = myContext.Cities.FirstOrDefault(x => x.city == "foo");
      Console.WriteLine(city.country.country); 
      // if lazy loading is enabled, `city.country` will not be null after this
    }
    

    请注意,我在 using 块内访问 city.country。如果您在释放上下文后尝试访问它(在using 块之外),它将无法工作(因为它没有上下文和数据库连接来检索数据)。

  3. 之后加载参考:

    var city = myContext.Cities.FirstOrDefault(x => x.city == "foo");
    myContext.Entry(city).Reference(x => x.country).Load();
    

至于为什么在您的示例中创建重复实例...如果您将导航属性 (country) 设置为未附加到您正在保存的上下文的对象,它会将其复制到数据库(它会将其保存为一个新实体,并为其分配一个新键)。您可以:

  1. 在保存之前将您的国家/地区附加到上下文中(此时不需要设置外键):

    ctx.Countries.Attach(country); // Attach the country to the context
    var city = new City { city = cityTxt, country = country };
    ctx.Cities.Add(city);
    
  2. 只需设置外键并让country 成为null(这就是为什么拥有外键属性很有用):

    var city = new City { city = cityTxt, countryId = country.id /* don't set country */ };
    ctx.Cities.Add(city);
    
  3. 预先从数据库(或缓存)加载国家:

    country = ctx.Countries.Find(country.id);
    var city = new City { city = cityTxt, country = country };
    city = ctx.Cities.Add(city);
    

Entity Framework 6 中的图形管理绝对落后于 Hibernate。它应该在 Entity Framework Core 中有更好的控制,但尽管有“Release Candidate”这个名字,EF Core 还没有准备好投入生产(老实说,考虑到他们在每个 RC 版本上来回更改所有内容,我会说现在不值得在真正的应用程序上进行投资,直到它解决...尽管这是有意见的,您的里程可能会有所不同。

如果你习惯了 Hibernate 的做事方式,你可能想看看NHibernate,你会发现它更熟悉。

PS:您的大小写令人困惑,C# 属性按惯例使用 TitleCase。当然你可以使用任何东西,但是如果你不习惯的话,你会发现大多数代码都很难阅读(我确实发现你的代码很难阅读,而且我很难用你的外壳编写示例)

【讨论】:

  • @GertArnold 好的和相关的答案,我会将它作为链接添加到我的答案中。谢谢!
  • 谢谢@Jcl。虽然我对 Java Hibernate 感觉很舒服,但我更喜欢尝试使用 EntityFramework,也许它在未来会有更多的连续性。感谢 TitleCase 的任命,我想按照 C# 的方式编写代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 2011-07-20
  • 1970-01-01
  • 2011-07-23
相关资源
最近更新 更多