关系、界面和 Entity Framework
Relationships, Interfaces and Entity Framework
使用 EF 建立关系和接口的最佳实践是什么?
我有一组现有的 TDD 类 和接口以及使用 Entity Framework Code First 作为 ORM 的要求。我有使用 EF 的经验,但对一对多和多对多关系的定义方式不满意。
令我惊讶的是,这似乎没有更多的记录(假设我没有搜索不正确的术语)。
例如,我有:
public interface IBar {
string BarValue { get; set; }
}
public class Bar : IBar {
string BarValue { get; set; }
}
public interface IFoo {
string FooValue { get; set; }
ICollection<IBar> Bars { get; set; }
}
public class Foo : IFoo {
public Foo()
{
Bars = new List<Bar>();
}
public string FooValue { get; set; }
public virtual ICollection<IBar> Bars { get; set; }
}
和
public interface IBar {
string BarValue { get; set; }
ICollection<IFoo> Foos { get; set; }
}
public class Bar : IBar {
public Bar()
{
Foos = new List<Foo>();
}
string BarValue { get; set; }
public virtual ICollection<IFoo> Foos { get; set; }
}
public interface IFoo {
string FooValue { get; set; }
ICollection<IBar> Bars { get; set; }
}
public class Foo : IFoo {
public Foo()
{
Bars = new List<Bar>();
}
public string FooValue { get; set; }
public virtual ICollection<IBar> Bars { get; set; }
}
我不确定我在这里是否正确,请问有什么想法吗?
我相信你已经知道了一点,因为它不在你的示例中,所以我想我会提到它:不要忘记你的空构造函数。 EF Code 首先使用这些来填充您的模型。
但是,您可能不希望它们暴露,所以让它们受到保护而不是 public。 EF 仍会看到受保护的构造函数,但不会看到私有构造函数。
你的一对多关系对我来说很好,但如果你想让延迟加载工作,它应该是虚拟的。
对于多对多,我的偏好是定义加入实体。我相信 EF 会在幕后用你所拥有的构建一个 join table。但是,我喜欢将我的导航属性保持在一边,以避免稍后解析数据时可能出现的递归问题。
一旦我拿到电脑,我将根据您的例子用一些例子来编辑这个。
好的,这是一对多的示例...我必须 运行 但今晚会 post 多对多,但扩展模式非常简单:
public interface IBar
{
string barValue { get; } // Values can sometimes be valuable in interfaces, see Foo's AddBar behavior.
void SetValue(string value); // Interfaces work best describing behavior, not structure.
}
public class Bar : IBar
{
public string barValue { get; private set; }
protected Bar() : this("") { } // EF
private Bar(string value) // Private Constructor
{
barValue = value;
}
public static Bar Create(string value = "") // Factory
{
return new Bar(value);
}
}
public interface IFoo
{
void SetValue(string value);
void AddBar(IBar bar);
}
public class Foo : IFoo
{
// Our Properties... note that private set does not interfere with EF at all, and helps encapsulate.
public string fooValue { get; private set; }
public virtual ICollection<Bar> bars { get; private set; }
// Constructor for EF, protected since we probably dont want to ever use it ourselves.
protected Foo() : this("") { }
// Our internal constructor, we will expose a factory for creating new instances (better control).
private Foo(string value)
{
fooValue = value;
bars = new List<Bar>();
}
// Our factory, great place to validate parameters, etc before even constructing the object.
public static Foo Create(string value = "")
{
return new Foo(value);
}
// Methods for controling Foo's properties:
public void SetValue(string value) { this.fooValue = value; }
public void AddBar(IBar bar) { this.bars.Add(Bar.Create(bar.value)); } // Leveraging Bar's factory
}
编辑:我最初使用它是因为示例中有它,但我通常不会在界面中指定属性。通常,接口应该被认为是描述行为而不是结构。事实上,实体框架无法使用属性接口(public virtual ICollection bars),因为它需要具有能够填充它的构造函数,当然接口没有构造函数。我相应地更新了示例并使界面更加行为驱动。今天晚上我仍然会添加很多例子。
好的,这是多对多。一些亮点:
我将 Foo 和 Bar 接口合二为一,因为它们都有相同的行为,这样更通用一些。请注意,您如何不能直接从带有 foobar 的 bar 一路遍历到 foo(本质上您会得到一个 Id 列表),这可以防止 运行 进入循环引用。您始终可以在任一侧添加一个或另一个的列表,并根据需要从您的存储库中手动填充它。
public interface IFooBar
{
string value { get; }
void SetValue(string value);
void AddFooBar(IFooBar item);
}
public class Bar : IFooBar
{
public string value { get; private set; }
public virtual ICollection<FooBar> foos { get; private set; }
protected Bar() : this("") { }
private Bar(string v) {
value = v;
foos = new List<FooBar>();
}
public static Bar Create(string value = "") { return new Bar(value); }
public void SetValue(string value) { this.value = value; }
public void AddFooBar(IFooBar foo) { this.foos.Add(FooBar.Create(this.value, foo.value)); }
}
public class FooBar
{
// assuming fooValue and barValue are the keys for Foo and Bar.
public String fooValue { get; private set; }
public String barValue { get; private set; }
protected FooBar() { }
private FooBar(String foo, String bar) {
fooValue = foo;
barValue = bar;
}
public static FooBar Create(String fooValue, String barValue) { return new FooBar(fooValue, barValue); }
}
public class Foo : IFooBar
{
public string value { get; private set; }
public virtual ICollection<FooBar> bars { get; private set; }
protected Foo() : this("") { }
private Foo(string v)
{
value = v;
bars = new List<FooBar>();
}
public static Foo Create(string value = "") { return new Foo(value); }
public void SetValue(string value) { this.value = value; }
public void AddFooBar(IFooBar bar) { this.bars.Add(FooBar.Create(this.value, bar.value)); }
}
此外,请记住,我们没有使用命名约定,因此您必须装饰或添加流畅的映射(推荐),以便 EF 识别键并建立关系...如果他们使用EF 的命名约定这可以部分避免,但您仍然需要为 FooBar 的多部分键添加一个映射,例如:
public class FooBarMap: EntityTypeConfiguration<FooBar>
{
public FooBarMap()
{
HasKey(k => new { k.barValue, k.fooValue });
}
}
然后将其添加到您的上下文配置中。
希望对您有所帮助...:)
使用 EF 建立关系和接口的最佳实践是什么?
我有一组现有的 TDD 类 和接口以及使用 Entity Framework Code First 作为 ORM 的要求。我有使用 EF 的经验,但对一对多和多对多关系的定义方式不满意。
令我惊讶的是,这似乎没有更多的记录(假设我没有搜索不正确的术语)。
例如,我有:
public interface IBar {
string BarValue { get; set; }
}
public class Bar : IBar {
string BarValue { get; set; }
}
public interface IFoo {
string FooValue { get; set; }
ICollection<IBar> Bars { get; set; }
}
public class Foo : IFoo {
public Foo()
{
Bars = new List<Bar>();
}
public string FooValue { get; set; }
public virtual ICollection<IBar> Bars { get; set; }
}
和
public interface IBar {
string BarValue { get; set; }
ICollection<IFoo> Foos { get; set; }
}
public class Bar : IBar {
public Bar()
{
Foos = new List<Foo>();
}
string BarValue { get; set; }
public virtual ICollection<IFoo> Foos { get; set; }
}
public interface IFoo {
string FooValue { get; set; }
ICollection<IBar> Bars { get; set; }
}
public class Foo : IFoo {
public Foo()
{
Bars = new List<Bar>();
}
public string FooValue { get; set; }
public virtual ICollection<IBar> Bars { get; set; }
}
我不确定我在这里是否正确,请问有什么想法吗?
我相信你已经知道了一点,因为它不在你的示例中,所以我想我会提到它:不要忘记你的空构造函数。 EF Code 首先使用这些来填充您的模型。
但是,您可能不希望它们暴露,所以让它们受到保护而不是 public。 EF 仍会看到受保护的构造函数,但不会看到私有构造函数。
你的一对多关系对我来说很好,但如果你想让延迟加载工作,它应该是虚拟的。
对于多对多,我的偏好是定义加入实体。我相信 EF 会在幕后用你所拥有的构建一个 join table。但是,我喜欢将我的导航属性保持在一边,以避免稍后解析数据时可能出现的递归问题。
一旦我拿到电脑,我将根据您的例子用一些例子来编辑这个。
好的,这是一对多的示例...我必须 运行 但今晚会 post 多对多,但扩展模式非常简单:
public interface IBar
{
string barValue { get; } // Values can sometimes be valuable in interfaces, see Foo's AddBar behavior.
void SetValue(string value); // Interfaces work best describing behavior, not structure.
}
public class Bar : IBar
{
public string barValue { get; private set; }
protected Bar() : this("") { } // EF
private Bar(string value) // Private Constructor
{
barValue = value;
}
public static Bar Create(string value = "") // Factory
{
return new Bar(value);
}
}
public interface IFoo
{
void SetValue(string value);
void AddBar(IBar bar);
}
public class Foo : IFoo
{
// Our Properties... note that private set does not interfere with EF at all, and helps encapsulate.
public string fooValue { get; private set; }
public virtual ICollection<Bar> bars { get; private set; }
// Constructor for EF, protected since we probably dont want to ever use it ourselves.
protected Foo() : this("") { }
// Our internal constructor, we will expose a factory for creating new instances (better control).
private Foo(string value)
{
fooValue = value;
bars = new List<Bar>();
}
// Our factory, great place to validate parameters, etc before even constructing the object.
public static Foo Create(string value = "")
{
return new Foo(value);
}
// Methods for controling Foo's properties:
public void SetValue(string value) { this.fooValue = value; }
public void AddBar(IBar bar) { this.bars.Add(Bar.Create(bar.value)); } // Leveraging Bar's factory
}
编辑:我最初使用它是因为示例中有它,但我通常不会在界面中指定属性。通常,接口应该被认为是描述行为而不是结构。事实上,实体框架无法使用属性接口(public virtual ICollection bars),因为它需要具有能够填充它的构造函数,当然接口没有构造函数。我相应地更新了示例并使界面更加行为驱动。今天晚上我仍然会添加很多例子。
好的,这是多对多。一些亮点: 我将 Foo 和 Bar 接口合二为一,因为它们都有相同的行为,这样更通用一些。请注意,您如何不能直接从带有 foobar 的 bar 一路遍历到 foo(本质上您会得到一个 Id 列表),这可以防止 运行 进入循环引用。您始终可以在任一侧添加一个或另一个的列表,并根据需要从您的存储库中手动填充它。
public interface IFooBar
{
string value { get; }
void SetValue(string value);
void AddFooBar(IFooBar item);
}
public class Bar : IFooBar
{
public string value { get; private set; }
public virtual ICollection<FooBar> foos { get; private set; }
protected Bar() : this("") { }
private Bar(string v) {
value = v;
foos = new List<FooBar>();
}
public static Bar Create(string value = "") { return new Bar(value); }
public void SetValue(string value) { this.value = value; }
public void AddFooBar(IFooBar foo) { this.foos.Add(FooBar.Create(this.value, foo.value)); }
}
public class FooBar
{
// assuming fooValue and barValue are the keys for Foo and Bar.
public String fooValue { get; private set; }
public String barValue { get; private set; }
protected FooBar() { }
private FooBar(String foo, String bar) {
fooValue = foo;
barValue = bar;
}
public static FooBar Create(String fooValue, String barValue) { return new FooBar(fooValue, barValue); }
}
public class Foo : IFooBar
{
public string value { get; private set; }
public virtual ICollection<FooBar> bars { get; private set; }
protected Foo() : this("") { }
private Foo(string v)
{
value = v;
bars = new List<FooBar>();
}
public static Foo Create(string value = "") { return new Foo(value); }
public void SetValue(string value) { this.value = value; }
public void AddFooBar(IFooBar bar) { this.bars.Add(FooBar.Create(this.value, bar.value)); }
}
此外,请记住,我们没有使用命名约定,因此您必须装饰或添加流畅的映射(推荐),以便 EF 识别键并建立关系...如果他们使用EF 的命名约定这可以部分避免,但您仍然需要为 FooBar 的多部分键添加一个映射,例如:
public class FooBarMap: EntityTypeConfiguration<FooBar>
{
public FooBarMap()
{
HasKey(k => new { k.barValue, k.fooValue });
}
}
然后将其添加到您的上下文配置中。
希望对您有所帮助...:)