在使用实体框架的分层架构中,我应该从 BLL 返回 POCO 类吗? (需要架构指导)
我可能读得太多了,并且遭受了一些信息超载的困扰。所以我希望得到一些明确的指导。
根据我收集的信息,我可以使用 VS2010 的 T4 模板来生成不直接与 EF 绑定的 POCO 类。我会将这些放在他们自己的项目中,而我的 DAL 将有一个 ObjectContext 派生类,对吧?
一旦我有了这些类,在 UI 层使用它们是否可以接受?也就是说,假设生成的类之一是 BookInfo
,它保存有关公共图书馆书籍的信息(标题、版本、页面、摘要等)。
我的 BLL 将包含一个类 BooksBLL
,如下所示:
public class BooksBLL
{
ObjectContext _context;
public void AddBook(BookInfo book) { ... }
public void DeleteBook(int bookID) { ... }
public void UpdateBook(int bookID, BookInfo newBook) { ... }
//Advanced search taking possibly all fields into consideration
public List<BookInfo> ResolveSearch(Func<BookInfo, bool> filter) { ... }
//etc...
}
因此,我的 MVVM UI 应用程序中的 ViewModel 将与上述 BLL 类进行通信并交换 BookInfo 实例。可以吗?
此外,MVVM 在 Web 上的帖子建议实现 IDataErrorInfo 来进行验证。如果我在生成的 POCO 类上实现所述接口可以吗?我从示例中看到那些生成的 POCO 类包含所有虚拟属性和内容,我希望添加我自己的逻辑可以吗?
如果有什么区别的话,目前我的应用程序不使用 WCF(或任何网络东西)。
另外,如果您发现我尝试构建 BLL 的方式存在严重错误,也请随时提供该领域的帮助。
更新(根据要求提供其他信息):
我正在尝试创建一个图书馆自动化应用程序。目前它不基于网络。
我正在考虑具有如下层:
- 由生成的 POCO 类(BookInfo、Author、Member、Publisher、Contact 等)组成的项目
- 具有 ObjectContext 派生类(DAL?)的项目
- 具有此类类的业务逻辑层我上面提到(BooksBLL、AuthorsBLL 等)
- 使用 MVVM 模式的 WPF UI 层。 (因此我提出了关于 IDataErrorInfo 实现的子问题)。
因此,我想知道诸如在 ViewModel 类中使用 BooksBLL
实例、调用 ResolveSearch()
来获取 List
等内容。 code> 并呈现它...也就是说,到处使用 POCO 类。
或者我应该有额外的类来反映从我的 BLL 公开的 POCO 类吗?
如果需要更多详细信息,请询问。
I've been reading too much probably and am suffering from some information overload. So I would appreciate some explicit guidance.
From what I've gathered, I can use VS2010's T4 template thingy to generate POCO classes that aren't tied directly to the EF. I would place these in their own project while my DAL would have an ObjectContext-derived class, right?
Once I have these classes, is it acceptable practice to use them in the UI layer? That is, say one of the generated classes is BookInfo
that holds stuff about books for a public library (Title, edition, pages, summary etc.).
My BLL would contain a class BooksBLL
for example like so:
public class BooksBLL
{
ObjectContext _context;
public void AddBook(BookInfo book) { ... }
public void DeleteBook(int bookID) { ... }
public void UpdateBook(int bookID, BookInfo newBook) { ... }
//Advanced search taking possibly all fields into consideration
public List<BookInfo> ResolveSearch(Func<BookInfo, bool> filter) { ... }
//etc...
}
So, my ViewModels in my MVVM UI app will be communicating with the above BLL class and exchanging BookInfo instances. Is that okay?
Furthermore, MVVM posts on the Web suggest implementing IDataErrorInfo
for validation purposes. Is it okay if I implement said interface on the generated POCO class? I see from samples that those generated POCO classes contain all virtual properties and stuf and I hope adding my own logic would be okay?
If it makes any difference, at present, my app does not use WCF (or any networking stuff).
Also, if you see something terribly wrong with the way I'm trying to build my BLL, please feel free to offer help in that area too.
Update (Additional info as requested):
I'm trying to create a library automation application. It is not network based at present.
I am thinking about having layers as follows:
- A project consisting of generated POCO classes (BookInfo, Author, Member, Publisher, Contact etc.)
- A project with the ObjectContext-derived class (DAL?)
- A Business Logic Layer with classes like the one I mentioned above (BooksBLL, AuthorsBLL etc)
- A WPF UI layer using the MVVM pattern. (Hence my sub-question about IDataErrorInfo implementation).
So I'm wondering about stuff like using an instance of BooksBLL
in a ViewModel class, calling ResolveSearch()
on it to obtain a List<BookInfo>
and presenting it... that is, using the POCO classes everywhere.
Or should I have additional classes that mirror the POCO classes exposed from my BLL?
If any more detail is needed, please ask.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您所做的基本上是存储库模式,实体框架和 POCO 非常适合该模式。
这正是 POCO 对象的用途;生成的类和手动编写它们的方式没有区别。您的 ObjectContext 封装了将任何更改保留回数据库的所有逻辑,并且不会直接暴露给您的 UI。
我个人并不熟悉
IDataErrorInfo
但如果现在您的实体仅在这个单个应用程序中使用,我认为没有任何理由不将其直接放入生成的类中。如果可能的话,将其添加到 T4 模板将是理想的选择,如果错误消息遵循任何逻辑模式,那么您将不必为每个类手动编码。无论如何,这并不是完全错误,但如果您计划针对 BLL 编写单元测试(我推荐),您将需要更改您的
ObjectContext
成员到IObjectContext
。这样,您就可以在运行时替换任何实现IObjectContext
接口的类(例如您的实际ObjectContext
),这将允许您进行测试内存中(即模拟)上下文,而不必访问数据库。同样,考虑将
List
替换为某种接口,例如IList
或IBindingList
或最小公分母IEnumerable
。这样,您就不会直接与特定的类List
绑定,并且如果您的需求随着时间的推移而发生变化(这种情况往往会发生),它将减少替换List所需的重构。 BookInfo>
与其他内容,假设您要替换它的任何内容都实现了您选择的接口。What you're doing is basically the Repository pattern, for which Entity Framework and POCO are a great fit.
That's exactly what POCO objects are for; there's no difference between the classes that are generated and how you would write them by hand. It's your ObjectContext that encapsulates all the logic around persisting any changes back to the database, and that's not directly exposed to your UI.
I'm not personally familiar with
IDataErrorInfo
but if right now your entities will only be used in this single app, I don't see any reason not to put it directly in the generated classes. Adding it to the T4 template would be ideal if that's possible, it would save you having to code it by hand for every class if the error messages follow any logical pattern.This isn't terribly wrong by any means, but if you plan to write unit tests against your BLL (which I would recommend), you will want to change your
ObjectContext
member toIObjectContext
. That way you can substitute any class implementing theIObjectContext
interface at runtime (such as your actualObjectContext
), which will allow you to do testing against an in-memory (i.e. mocked) context and not have to hit the database.Similarly, think about replacing your
List<BookInfo>
with an interface of some kind such asIList<BookInfo>
orIBindingList<BookInfo>
or the lowest common denominatorIEnumerable<BookInfo>
. That way you're not tied directly to the specific classList<T>
and if your needs change over time, which tends to happen, it will reduce the refactoring necessary to replace yourList<BookInfo>
with something else, assuming whatever you're replacing it with implements the interface you've chosen.您不需要做任何特别的事情...正如马克所说,没有“正确”的答案。但是,如果您的应用程序足够简单,您只需复制您的类(例如 BookInfoUI 和 BookInfoBLL),那么我建议仅使用业务类。额外的层没有任何作用,因此它不应该存在。如果您的应用程序很简单并且业务逻辑很少,那么 DDD 中的 Eric Evans 甚至建议将所有逻辑都放在 UI 层中。
为了进行区分,应用程序层应该具有对应用程序中发生的情况进行建模的类,而领域层应该具有对域中发生的情况进行建模的类。例如,如果您有一个搜索页面,您的 UI 层可能会从应用程序层中的 BookSearchService 检索 BookSearchResult 对象列表,这将使用域来拉取 BookInfo 列表。
You don't need to do anything in particular... as Mark said, there is no "right" answer. However, if your application is simple enough that you would simply be duplicating your classes (e.g. BookInfoUI & BookInfoBLL), then I'd recommend just using the business classes. The extra layer wouldn't serve a purpose, and so it shouldn't exist. Eric Evans in DDD even recommends putting all your logic in the UI layer if you app is simple and has very little business logic.
To make the distinction, the application layer should have classes that model what happens within the application, and the domain layer should have classes that model what happens in the domain. For example, if you have a search page, your UI layer might retrieve a list of BookSearchResult objects from a BookSearchService in the application layer, which would use the domain to pull a list of BookInfo.
您的问题的答案可能取决于您的应用程序的规模和复杂性。所以我担心也会有有效的论据来回答你的问题是和否。
就我个人而言,我会用“是”回答您的两个主要问题:
在 UI 层中使用 POCO(域)类是否可以接受?
我想“UI 层”实际上并不是指 View MVVM 模式的一部分,但 ViewModel 除外。 (我相信,大多数 MVVM 专家都会反对让 View 直接引用 Model。)
将 Domain 项目中的 POCO 作为属性包装到 ViewModel 中并将此包装的 POCO 直接绑定到 View 并不罕见。最大的优点是:很简单。您不需要在 ViewModel 中添加额外的 ViewModel 类或复制属性,然后在对象之间复制这些属性。
但是,如果您使用 WPF,则必须考虑到,如果将 POCO 属性绑定到视图,绑定引擎将直接写入 POCO 属性。这可能并不总是您想要的,特别是当您在 WPF 表单中使用附加的和更改跟踪的实体时。您必须考虑取消场景或如何在取消后恢复已被绑定引擎更改的属性。
在我当前的项目中,我正在使用分离的实体:我从数据层加载 POCO,将其与上下文分离,处理上下文,然后在 ViewModel 中使用该副本并将其绑定到视图。数据层中的更新是通过创建新上下文、按 ID 从数据库加载原始实体,然后更新绑定到视图的更改后的 POCO 的属性来实现的。因此,通过这种方法,附加实体的意外更改问题就消失了。但与独立的实体合作也有缺点(例如更新更复杂)。
如果我在生成的 POCO 类上实现
IDataErrorInfo
接口可以吗?如果您将 POCO 实体绑定到视图(通过包装 ViewModel),它不仅可以,而且如果您想利用 WPF 绑定引擎的内置属性验证,您甚至必须在 POCO 类上实现
IDataErrorInfo
。尽管此接口主要与 UI 技术一起使用,但它是 System.ComponentModel 命名空间的一部分,因此不直接绑定到任何 UI 命名空间。基本上,IDataErrorInfo
只是一个简单的协定,它支持报告对象的状态,这在 UI 上下文之外也可能有用。对于
INotifyPropertyChanged
接口也是如此,如果您将 POCO 类直接绑定到视图,您还需要在 POCO 类上实现该接口。我经常看到由于某些架构原因而与我意见不一致的观点。但这些观点都没有认为另一种方法更容易。如果您严格希望避免在 ViewModel 层中使用 POCO 模型类,则需要添加另一个映射层,这会带来额外的复杂性以及编程和维护工作量。所以我会投票:只要你没有令人信服的理由和明显的好处来使你的架构变得更加复杂,就保持简单。
Answers to your questions may depend on the size and complexity of your application. So I am afraid there will be valid arguments to answer your questions with Yes and No as well.
Personally I will answer your two main questions both with Yes:
Is it acceptable practice to use POCO (Domain) classes in the UI layer?
I guess with "UI layer" you don't actually mean the View part of the MVVM pattern but the ViewModels. (Most MVVM specialists would argue against letting a View directly reference the Model at all, I believe.)
It is not unusual to wrap a POCO from your Domain project as a property into a ViewModel and to bind this wrapped POCO directly to the View. The big Pro is: It's easy. You don't need additional ViewModel classes or replicated properties in a ViewModel and then copy those properties between the objects.
However, if you are using WPF you must take into account that the binding engine will directly write into your POCO properties if you bind them to a View. This might not always be what you want, especially if you are working with attached and change-tracked entities in a WPF form. You have to think about cancellation scenarios or how you restore properties after a cancellation which have been changed by the binding engine.
In my current project I am working with detached entities: I load the POCO from the data layer, detach it from context, dispose the context and then work with that copy in the ViewModel and bind it to the View. Updating in the data layer happens by creating a new context, loading the original entity from the DB by ID and then updating the properties from the changed POCO which was bound to the View. So the problem of unwished changes of an attached entity disappears with this approach. But there are also downsides to work with detached entites (updating is more complex for instance).
Is it okay if I implement the
IDataErrorInfo
interface on the generated POCO class?If you bind your POCO entities to a View (through a wrapping ViewModel) it is not only OK but you even must implement
IDataErrorInfo
on the POCO class if you want to leverage the built-in property validation of the WPF binding engine. Although this interface is mainly used together with UI technologies it is part ofSystem.ComponentModel
namespace and therefore not directly tied to any UI namespaces. BasicallyIDataErrorInfo
is only a simple contract which supports reporting of the object's state which also might be useful outside of a UI context.The same is true for the
INotifyPropertyChanged
interface which you also would need to implement on your POCO classes if you bind them directly to a View.I often see opinions which would disagree with me for several architectural reasons. But none of those opinions argue that another approach is easier. If you strictly would want to avoid to have POCO model classes in your ViewModel layer, you need to add another mapping layer with additional complexity and programming and maintenance effort. So I would vote: Keep it simple as long as you do not have a convincing reason and clear benefit to make your architecture more complex.