In 《Entityframework-Based Domain-driven design practices [continued]: Use Cases of domain-driven design practices based on EF 4.3.1 code firstIn the article, I gave a field-driven design practice case based on Entity Framework 4.3.1 code first: byteart retail. This case has attracted the attention of many readers and many netizens have asked questions about various implementation technologies in the case. I also answered questions one by one. In order to better demonstrate the domain-driven design based on Microsoft. in terms of net technology, I have further improved byteart retail. Now I release the byteart retail case (byteart retail V2 for short) in the future version for your reference.
Compared with the previous byteart retail case, the demo case of the new version (V2) has the following improvements:
- Chinese comments (Continuous Improvement)
- Use of existing databases
- Unity-based WCF per-request lifetime Manager
- Warehousing interface for specific needs
- Specific implementation of the Statute
- Unity-based AOP Interception
- Use log4net to record the intercepted exception details
In the following sections, we will briefly introduce the above content.
Byteart retail V2 Case Study Source code Download
Please[Click here]Download byteart retail case V2 SourceCode
Deployment and running
- Uncompress byteart retail v2
- Create a database named byteartretail in the SQL Server database
- Run the byteartretail. sqldatabase script in the SQL directory. This will create a data table related to this case.
- Open the byteart retail. sln solution in Visual Studio 2010 and open the Web. config file under the byteartretail. Services Project.
- Change the database connection string used by the Entity Framework Based on your database configuration. Note that the Mars option is enabled.
- In byteartretail. under the Services Project, find any one. SVC file, right-click and select View in browser menu, which starts ASP. net Development Server, and open the selected WCF Service page in the browser
- Start byteartretail. Web project to display the user interface
Note 1: In the previous version (V1), incorrect database initialization policies were used, the error message "Entity Framework error" is reported after the database is created (the error message returned when the migration database does not exist ). In V2, byteartretaildbcontext no longer uses any initialization policy when initializing the database. This solves the above problems in V1.
Note 2: There is no difference between V2 and V1 on the user interface and functions.
Improved Chinese comments for V2 functions (continuously improved)
According to feedback from netizens in article V1, I will gradually use Chinese annotations to replace the original English annotations from V2, but there are many source code files in the project, I usually have limited personal time, so I can't finish all the updates at one time. I can only continue to improve in future version upgrades. Of course, I will also take the time to gradually improve and update the comments in the current version during the version upgrade process.ArticleDownload link in, so you can only hope that netizens: Please take care of more + please forgive me + welcome to follow.
Use of existing databases
In V2, the initialize public static method of byteartretaildbcontextinitializer type is updated (this method is located in byteartretail. domain. repository project, byteartretail. domain. repositories. in the entityframework namespace), no database initialization policy is used during database initialization to enable the use of existing databases. This makes it easier for readers to deploy and run this case.Program.
Namespace byteartretail. domain. repositories. entityframework {// <summary> // indicates that the data access context initiator dedicated to byteart retail is used. /// </Summary> Public sealed class initialize: dropcreatedatabaseifmodelchanges <byteartretaildbcontext> {// when using byteartretaildbcontextinitializer as the database initializer, remove the following code line // comments so that the corresponding SQL script is executed when the database is rebuilt. For existing databases, comment out the following code lines. // Protected override void seed (byteartretaildbcontext context) // {// context. database. executesqlcommand ("create unique index idx_customer_username on customers (username)"); // context. database. executesqlcommand ("create unique index idx_customer_email on customers (email)"); // context. database. executesqlcommand ("create unique index idx_laptop_name on laptops (name)"); // base. seed (context );//}/// <Summary> // initialize the database. /// </Summary> Public static void initialize () {database. setinitializer <byteartretaildbcontext> (null );}}}
Unity-based WCF per-request lifetime Manager
This improvement comes from the consistency problem of repositorycontext in the same request. In a WCF operation context, in many cases, the task coordination meetings at the application layer involve multiple repository, and these repository should all share the same repositorycontext, so that all operations can be submitted once through repositorycontext to complete the unit of work. In the V1 case, the repository contextmanager is used to ensure the consistency of the repositorycontext instance, and then the repositorycontextmanager is used. the getrepository method returns the warehouse instance targeting the specified aggregation root. Although this ensures the consistency of the repositorycontext instance, it also loses the scalability of the Repository: we can only use the repository implementation of the entityframeworkrepository generic type, and its warehousing methods are extremely limited.
Therefore, V2 uses the unity-based WCF per-request lifetime manager to solve such conflicts. Since the WCF Service layer uses the unity IOC container to obtain the specific implementation of the application layer (for applications in servicelocator mode ), therefore, at the application layer, you can obtain the repositorycontext and repository instances injected by unity through the constructor, in this case, the lifecycle of the repositorycontext is managed by the WCF per-request lifetime Manager (each time a WCF request is initiated, a new instance is resolve, and the instance is destroyed after the WCF request processing is completed ). We can get a general idea of this from the following code snippets:
/// <Summary> /// represents an implementation of the application layer services related to the customer. /// </Summary> public class customerserviceimpl: applicationservice, icustomerservice {# region private fields private readonly extends customerrepository; private readonly extends shoppingcartrepository; private readonly extends salesorderrepository; # endregion # region ctor // <summary> // Initialize an instance of the <C> customerserviceimpl </C> type. /// </Summary> /// <Param name = "context"> is used to initialize a <C> customerserviceimpl </C> type warehouse context instance. </Param> /// <Param name = "customerrepository"> "customer" Storage instance. </Param> /// <Param name = "shoppingcartrepository"> "Shopping Cart" Storage instance. </Param> /// <Param name = "salesorderrepository"> "sales order" warehousing instance. </Param> Public customerserviceimpl (irepositorycontext context, icustomerrepository customerrepository, ishoppingcartrepository shoppingcartrepository, isalesorderrepository salesorderrepository): Base (context) {This. customerrepository = customerrepository; this. shoppingcartrepository = shoppingcartrepository; this. salesorderrepository = salesorderrepository;} # endregion # region icustomerservice Members /// <summary> /// create a customer object based on the given customer information. /// </Summary> /// <Param name = "dataobject"> data transmission object that contains customer information. </Param> /// <returns> the Globally Unique Identifier of the created customer object. </Returns> Public guid createcustomer (customerdataobject dataobject) {If (dataobject = NULL) throw new argumentnullexception ("customerdataobject"); If (customerrepository. usernameexists (dataobject. username) throw new domainexception ("customer with the username of '{0}' already exists. ", dataobject. username); If (customerrepository. emailexists (dataobject. email) throw new domainexception ("customer with the email of '{0}' already exists. ", dataobject. email); customer = mapper. map <customerdataobject, Customer> (dataobject); shoppingcart = customer. createshoppingcart (); customerrepository. add (customer); shoppingcartrepository. add (shoppingcart); context. commit (); Return customer. ID;} // ***** ignore other code ***** # endregion}
In the Web. config of the byteartretail. Services project, set the lifetime manager of irepositorycontext to wcfperrequestlifetimemanager. The specific implementation code of wcfperrequestlifetimemanager can be found in the byteartretail. Infrastructure Project:
<! -- Repository Context & repositories --> <register type = "byteartretail. domain. repositories. irepositorycontext, byteartretail. domain "mapto =" byteartretail. domain. repositories. entityframework. entityframeworkrepositorycontext, byteartretail. domain. repositories "> <lifetime type =" byteartretail. infrastructure. wcfperrequestlifetimemanager, byteartretail. infrastructure "/> </register>
Warehousing interface for specific needs
Because V2 decouples the specific implementation of repositorycontextmanager and repository, we can easily customize the warehousing interface for specific needs. In the repositories subdirectory of the byteartretail. Domain project, a storage interface similar to ixxxrepository (such as icustomerrepository and isalesorderrepository) is added, which implements the pository generic interface.
Byteartretail. domain. the repositories project contains implementation classes for these ixxxrepsitory interfaces. These classes not only implement the ixxxrepository interface, but also inherit from the entityframeworkrepository generic class, this allows you to directly use the defined standard warehousing operations. In the comment section on article V1, A friend suggested how to operate the standard warehousing interface if you want to sort Multiple object attributes. In V2, The getalllaptops method of laptoprepository provides the answer:
Public ienumerable <laptop> getalllaptops () {var query = efcontext. context. set <laptop> (). orderby (L => L. unitprice ). thenby (L => L. name); Return query. tolist ();}
Another benefit of this implementation method is that when I find that other fields need to be sorted in the future, I can re-implement the ilaptoprepository interface and handle the Sorting Problem in the implementation class, you do not need to modify the laptoprepository class or even the ilaptoprepository interface to enable it to sort other fields.
Specific implementation of the Statute
In the source code of V1, all the protocols passed to repository are generated by passing in a Lambda expression through the eval method of the specification generic class. In V2, these codes are replaced by specific implementations of the Protocol: we can find these Implementation classes in the specifications directory of the byteartretail. domain. repositories project.
On the surface, Eval makes programming easier, and the specific implementation of the Protocol is essentially a Lambda expression. In fact, such a change is based on the following considerations:
- The specific implementation Class Name of the statute clearly expresses the motive of the statute, which facilitates the participation of the Statute as an element of the general language in the field-oriented discussion.
- The implementation of object-oriented specification facilitates the application of the model and further explores the feasibility of implementing dynamic warehousing query.
Unity-based AOP Interception
V2 uses an extension of unity to implement AOP interception. The extension is unity interception extension, which can be found in nuget Package Manager. To use the unity Interception Function, you must not only add a reference to unity, but also add a reference to unity interception extension.
To demonstrate AOP interception, V2 defines an interception behavior: exceptionloggingbehavior, which is used to write exception information to the log file when an exception occurs at the application layer. The source code for this interception is located in the inteceptionbehaviors directory of the byteartretail. Infrastructure Project. The caught exceptions are handled using the utils tool class in the invoke method.
In the Web. config file of the byteartretail. Services project, when registering the unity container, We need to specify the interceptor type and interception behavior for the interface type at the application layer:
<Register type = "byteartretail. application. icustomerservice, byteartretail. application "mapto =" byteartretail. application. implementation. customerserviceimpl, byteartretail. application "> <interceptor type =" interfaceinterceptor "/> <interceptionbehavior type =" byteartretail. infrastructure. interceptionbehaviors. predictionloggingbehavior, byteartretail. infrastructure "/> </register>
Use log4net to record the intercepted exception details
V2, combined with the AOP interception of unity, uses log4net to record the exception information generated by the application layer. The following points should be noted:
- In the assemblyinfo. CS file of the byteartretail. Services Project, specify the configuration source of log4net:
[Assembly: log4net. config. xmlconfigurator (watch = true)]
- In the global. asax. CS file of the byteartretail. Services Project, initialize the log4net framework:
Protected void application_start (Object sender, eventargs e) {byteartretaildbcontextinitailizer. initialize (); applicationservice. initialize (); log4net. config. xmlconfigurator. Configure ();}
- In the Web. config of the byteartretail. Services Project, configure log4net. For more information, see this document.
Is the log information generated in the byteartretail. Services \ logs directory:
Summary
This article briefly introduces the domain-driven design case based on Entity Framework code first: some changes and new features of byteart retail V2, readers can use the link provided in this article to download the source code of V2, if you have any questions or suggestions, please leave a message. In the next version of byteart retail, I will continue to study the distribution of domain events, Enterprise Service Bus (ESB), system integration, anti-corrosion layer, and other topics.