Nhib.pdf beginner's Guide (20): Common Errors in development (1)

Source: Internet
Author: User

This article and the next article discuss some common errors that developers encounter when using nhib.pdf for development.

Clear request

Nhibernate, fluentnhibernate, or conform all define many conventions on domain model ing. However, many people still define lengthy mappings. Too many definitions, especially unnecessary ones, will blur important ing elements, making them hard to read and understand.

Let's take a look at the following completely legal XML ing snippet:

 <  Property  Name  = " Orderdate " Lazy  = " False " Access  = " Property "Type  = " Datetime " Insert  = " True " Update  = " True " Unique  = " False " > <  Column  Name  = "Orderdate " Not-Null  = " True " SQL-type  = " Datetime " /> </  Property  > 

Although the clip is long, it only defines the ing of a single attribute of an object. In this example, the orderdate attribute of the Order object is mapped.

You can use the followingCodeGet the same result:

<PropertyName="Orderdate"Not-Null="True"/>

Obviously, the latter is more readable, easy to maintain, and has fewer errors.

Let's take a closer look at the unnecessary features and explain why they are unnecessary in most cases:

    • Lazy: by default, all objects mapped with the <property> tag are not delayed, so false is the default value.
    • Access: nhibter uses setter and getter of the attribute to access object data by default, so there is no need to display the declaration.
    • Type: nhib.pdf uses reflection to determine the type of the ing attribute. In most cases, this is the expected behavior. Only a few cases require us to help Nhibernate determine the correct attribute type.
    • Insert: by default, the value of the ing attribute is used to insert a new record to the database. In other words, the default value of insert is true.
    • Update: Like insert, the value of the ing attribute is used to update existing records in the database. The default value of update is true.
    • Unique: default value. nhib.pdf assumes that the value of the given database column must not be unique. Therefore, the default value of unique is false.
    • Column name: When ing fields in the underlying database table, the default name is the same as the attribute name.
    • Column SQL-type: In most cases, nhib.pdf maps the. NET data type correctly to the type used by the underlying database.

Therefore, my suggestion is: declare only the absolute minimum value of the requirement and use as many default constraints as possible.

Error ing read-only access

Generally, we may need to access some data in read-only mode. For example, the data can be nonstandard or aggregated on the screen or on paper to generate reports. In this case, we may want to use the database view to retrieve data and map data transmission objects to these Views.

A typical example is to retrieve the list of the names, order quantities, and total orders of the first 10 customers in a month. Suppose we have created a database view to collect the data. This view is called top10mermersofmonth. It has the customerid, customername, nbroforders, totals, month, and year fields.

We also assume that the data transmission object top10mers MERs wants to map the data transmission object to the view. This time we use fluent Nhibernate ing. The Code is as follows:

 
Public classTop10customersmap:Classmap<Top10mers MERs> {PublicTop10customersmap () {table ("Top10customersofmonth"); Id (x => X. customerid ). generatedby. guidcomb (); Map (x => X. customername ). length (50 ). not. nullable (); Map (x => X. nbroforders ). not. nullable (); Map (x => X. total ). not. nullable (); Map (x => X. month ). not. nullable (); Map (x => X. year ). not. nullable ();}}

Despite the code above, some code is inefficient and lengthy. The correct ing should be as follows:

Public classTop10mermersmap2: Classmap <top10mers MERs> {PublicTop10customersmap2 () {table ("Top10customersofmonth"); Readonly (); Not. lazyload (); schemaaction. none (); Id (x => X. customerid); Map (x => X. customername); Map (x => X. nbroforders); Map (x => X. total); Map (x => X. month); Map (x => X. year );}}

At first we said that this view is read-only, so we call the readonly () method to tell nhib.pdf.

Because the database view provides a non-normalized and clustered data view for quick data retrieval, we do not want to use delayed loading in this example, so we call not. lazyload (). To avoid delayed loading, nhib.pdf does not need to create a proxy to encapsulate data transmission objects, so it is more efficient.

If we use the schemaexport class of nhib.pdf to generate a database architecture from the domain model, we must tell nhib.pdf not to generate a table for the above ing. Note that nhib.pdf is not supported and it will never create a view correctly. The metadata provided by the ing file is insufficient to create a view. This is why we add the schemaaction. None () statement.

When defining a ing between a data transmission object and a database view or table for read-only access, there is no need to define elements other than name. In a few cases, type is defined, there is no need to define a policy to generate an ID, because no ID is created and only data is read. We do not need to define whether the field can be empty or the length of the string type field.

Blind dependency on nhib.pdf

Nhib.pdf is a very useful tool that reduces the workload of our developers when processing data that persists to relational databases. However, nhib.pdf is just a tool. Nhibernate uses the best policy to access data in most cases, but we cannot blindly rely on Nhibernate.

Most of the time, nhib.pdf cannot make the right decision for us. In this case, we need to help it and give it context prompts.

One possible scenario is that if we want to retrieve the list of parent entities, each of them has a collection of child entities. During the operation, if we want to access all the attributes of the Child body, we 'd better tell nhib.pdf that we want to do this so that it can load all the objects at a time instead of relying on the default delayed loading behavior. We will discuss the select (n + 1) issue later.

Another case is that we have a parent entity which has two different child entity sets. For example, a blog entity has a set of post and author. If we force Nhibernate to load the blog entity and its two child sets in one request. A cross join basically combines each record of one table with each record of another table. As a result, too much data is loaded from the database. If two tables contain many records, this puts a lot of pressure on the system.

Use implicit transactions

We strongly recommend that you use the display transaction to encapsulate database operations when using nhib.pdf. If the transaction boundary is not displayed, the database uses an implicit transaction to encapsulate each separate statement. In most cases, this is inefficient because we usually execute multiple statements in a business transaction. Because the boundary of business transactions is defined according to the functions we implement, we should always display the opening or closing of transactions at the appropriate position. The simplest way to do this is to start a transaction directly after opening a session and commit the transaction directly before closing the session.

BeforeArticleIn this example, we use this simple method:

 
Using (VAR session = sessionfactory. opensession) using (VAR Tx = session. begintransaction () {// do some database operations Tx. Commit ();}

For Web ApplicationsProgramYou may want to start a transaction in beginrequest in the global. asax file and submit the transaction in endrequest.

Use data to generate ID

Generating IDs for our entities using databases is very attractive and straightforward at first glance. However, taking a closer look at the problem at hand shows that using the database to generate IDS is an effective anti-pattern and should be strictly avoided.

The database Generation ID is meaningful only when our program and other programs that can update the data in it share the database. In this case, we may want to rely on the database to generate the necessary primary keys to ensure their uniqueness. However, we have discussed earlier that we should prevent this situation and insist that our programs own databases.

Nhib.pdf provides us with many ways to create or define primary key values for new entities. I personally recommend two ID generators: HiLo generator and guidcomb generator.

The HiLo generator generates an integer as the primary key value, while the guidcombAlgorithmThe GUID generated to optimize the indexes in the database. The GUID generated using the standard algorithm of the. NET Framework is not suitable, so Nhibernate provides its own implementation in the form of a guidcomb generator.

For example, if we are using an architecture model called command query responsibility segregation (cqrs) and its query operations and data operations are strictly separated, we may even consider using the client to generate IDS. The GUID is the only reasonable type that ensures the uniqueness of the ID during planting. It is relatively simple to implement a guid algorithm that can be used by a client and generated for index optimization in the database. One way is to copy the code used by nhib.pdf. This should not be a problem becauseSource codeYes.

The method of using the LINQ to nhib.pdf error is as follows:

LINQ to nhib.pdf is a perfect complement to the framework, which makes it very easy to query databases. You can also use the hql, criteria query, or queryover syntax to perform a LINQ query. In addition, the query by LINQ to nhib.pdf can be combined like the query by LINQ to objects.

However, this API may also be used improperly. To make full use of LINQ, you must understand the system boundaries.

You can process iqueryable result sets in the context of LINQ to nhibable.

LINQ queries are always delayed. That is, we can define a LINQ query, and the system does not execute it in the view. For example, the following statement does not cause the system to execute a query:

VaRProducts = session. query <product> (). Where (P => P. discontinued );

When we start to traverse the results, the query will be executed:

 
Foreach (VAR product in products) {// do something with product}

The data source of LINQ to nhib.pdf is a database. When we traverse such a query, the hibernate LINQ provider parses the query expression tree to generate an SQL statement and then runs it in the database.

As shown in the preceding figure, the LINQ query can be combined. If we want to group the above query by the category name, and print the cateogry name list, the category name is followed by the name and unit price of all its products, we can do this:

 Using ( VaR Session = factory. opensession ()) Using ( VaR Tx = session. begintransaction ()){ VaR Products = session. query <product> (). Where (P => P. discontinued ); Foreach ( VaR Productgroup In Products. groupby (P => P. Category. Name )){ Console . Writeline ( "Category: {0 }" , Productgroup. Key ); Foreach ( VaR Product In Productgroup ){ Console . Writeline ( "{0}-{1 }" , Product. Name, product. unitprice );}}}

In the code snippet above, we applied the groupby operation to the product query obtained previously. Then we traverse the product group and print the category name, and then list all the products in that group.

If we run the code, the following query will be sent to the database:

This triggers an exception because the query is incorrect. Only fields that appear in the group by statement can appear in the select list. However, we need all the fields, so we must find another solution.

We need all the products, so we can force Nhibernate to load them before the groupby operation. Therefore, we can add an asenumerable () Statement, as shown in the following code:

 
VaRProductgroups = session. query <product> (). Where (P => P. discontinued). asenumerable (). groupby (P => P. Category. Name );

Now the query can be executed, and the expected results can be printed on the screen.

The asenumerable statement is used to convert the context from LINQ to nhib.pdf to LINQ to objects without triggering query execution. Now, whenever we traverse the query results, the LINQ provider knows which part of the Expression Tree is converted into an SQL statement and can be executed in the database.

The trouble of delayed Loading

Delayed loading is a very convenient feature provided by nhib.pdf, but if you do not pay attention to it, it will lead to slow system.

If we define different entities of a domain model to be correlated by referencing or one-to-many relationships, delayed loading is very useful. When we only process a specific object, delayed loading will prevent Nhibernate from always loading all associated entities. Let's look at a simple blog engine. There is a blog entity that has a set of post entities, as shown in. By default, the post entity set of each blog entity is loaded only when necessary, that is, they are delayed loading.

Select (n + 1)

When we use the following code to load the blog entity from the database:

 
Using(VaRSession = factory. opensession ())Using(VaRTx = session. begintransaction () {blog = session. Load <blog> (1001); Tx. Commit ();}

Nhibernate generates the following query:

Note that only the blog table is queried, and the Post table is not accessed yet. In our code, as long as only the attributes of the blog entity (such as name and author) are accessed, post will not be loaded.

When we try to use the following code to access the post set of a blog:

VaRNbrofposts = blog. Posts. count;

An interesting thing happened. Nhib.pdf creates another query to retrieve all posts associated with the blog:

Suppose we want to generate a report and print the names and authors of all blogs. Each blog is followed by a list of titles of all its posts. The following code is used to complete the process:

Using(VaRSession = factory. opensession ())Using(VaRTx = session. begintransaction ()){VaRBlogs = session. query <blog> ();Foreach(VaRBlogInBlogs ){Console. Writeline ("Blog {0} By {1 }", Blog. Name, blog. Author );Foreach(VaRPostInBlog. Posts ){Console. Writeline ("{0 }", Post. Title) ;}} Tx. Commit ();}

The result is shown in:

In the previous example, we had three different blogs. Each blog has some posts. Using the previous code snippet, nhib.pdf creates a query to retrieve all the blogs, and then creates an additional query to retrieve the post of this blog for each blog. In mathematics, if we create n blogs, nhib.pdf creates (n + 1) queries in total. This is the query problem of select (n + 1.

There are only a few blogs, which may be a small problem. However, if there are hundreds or even thousands of blogs, this is a big problem. But don't worry. When we load a blog, we can solve this problem by notifying nhib.pdf to pre-load all the post files. The Code is as follows:

  using  ( var  session = factory. opensession ()  using  ( var  Tx = session. begintransaction () { var  blogs = session. query 
  
    (). fetch (B => B. posts); 
    foreach  (
    var  blog 
    in  blogs) {
    console . writeline (
    "blog {0} By {1}" , blog. name, blog. author); 
    foreach  (
    var  post 
    in  blog. posts) {
    console . writeline (
    "{0}" , post. title) ;}} Tx. commit () ;}
  

In the previous LINQ to nhib.pdf query, we used the fetch method to specify that post is preloaded with the blog. You only need to change this place, and the rest will remain unchanged.

After the modification, the query created by nhib.pdf is shown in:

Note that only one query is generated to retrieve all blogs and all posts.

Access delay loading after session is closed

When nhib.pdf is used, another developer may easily make a mistake: when they try to access a property or set of objects that are delayed to be loaded, the session used to retrieve objects has been closed. In this case, nhib.pdf throws an exception, as shown in:

How can we avoid this situation? There are several possible methods:

    • Close the session whenever possible after all data has been loaded and used. In a web application, if you use a session model for each request, you can open the session at the beginning of the request and close the session at the end of the request.
    • Enable Nhibernate to pre-load the data that was originally delayed.
    • Use the nhibernateutil class to force the delayed loading of the set or attribute initialization. In the blog example, the code is nhibernateutil. initialize (blog. Posts );.
    • Load data using the database view. When ing these views, avoid using delayed loading.
Have you loaded the entire database?

If we have a complex domain and use this domain model to query data, it is easy for a Nhibernate request to trigger thousands of queries in the database. All these queries are caused by delayed loading.

In a domain model, it is easy to define the relationships in an object model from one entity to another. The following code seems harmless, but it will cause a lot of trouble in the database:

 
Lineitem. Order. Customer. customername

It is important to limit the number of decimal places in an expression.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.