The last lecture was very depressing, and I was also depressed. The whole series had no detailed code, so it was a bit confusing. In fact, you should be able to understand the entire idea in the previous sections. I will repeat it here: After the user accesses the URL-> get the current page according to the URL rules of all pages (urlpattern), and then according to the template of the page, find all labels (via reflection), traverse these labels, and replace the HTML of the obtained data with the labels of the label. Code . Finally, the entire template is the HTML to be generated. Therefore, we need to understand how the label obtains the data of the database. A label can be understood as a control, so the control will certainly support some attributes (parameter) and some content (field ). In the previous section, we will talk about how to resolve the parameter. Because of some special parameter, we designed the base class of the parameter during design, and the special one is the subclass. Similarly, field is a specific field to be displayed in HTML code, for example, [field: Title/]. This is a field, our work of the template engine is to replace the title that should be displayed, and how can we work? We have to design the entire logic of the field. Use the field class when replacing the loop. However, I will not talk about field today, because we are still confused. Today, we will design a primary version of article: list. I think it is easier to understand the design concept from examples. OK, then we will design an article. List, which we are most familiar. Article List.
// Simple loop list {Article: List Top = "10" categoryid = "5 "} <A href = "/details/ [Field: Filename/] "Target =" _ blank "> [Field: Title/] </A>
{/Article: List}
Imagine that the repeater has an itemtemplate. For the label list, its itemtemplate is obviously the template attribute. If we get the data source, we can directly replace all fields with foreach. The code is roughly as follows:
/// <Summary> /// obtain the HTML code to be displayed /// </Summary> /// <returns> </returns> Public override string getrenderhtml () {var html = templatestring; foreach (VAR article in getdatasource () {foreach (VAR field in fields) {html = html. replace (field. HTML, field. getvalue (Article) ;}} return HTML ;}
From the above method, we can see that the replacement mechanism is that all fields must be replaced once for each row of data (so we mentioned that templatestring should be processed to prevent field confusion when constructing nesting ), finally, HTML is returned. We can also see some unknown methods and fields: getdatasource (), field. html, field. getvalue (), which have exposed part of our field design. Let's first look at what getdatasource () is?
/// <Summary> /// obtain the article list /// </Summary> /// <returns> </returns> private ienumerable <Article> getdatasource () {VaR parameter = new articlequeryparameter (); // construct the query parameter return articledatahelper. getlist (parameter) ;}/// <summary> /// query the parameter base class /// </Summary> public class queryparameter {public int pagesize {Get; set ;} public int pageindex {Get; set;} public int recordcount {Get; set;} Public String searchkey {Get; Set ;}} /// <summary> // Article Query Class // </Summary> public class articlequeryparameter {public queryparameter pageparameter {Get; set;} public datetime posttime {Get; set ;} public int categoryid {Get; set;} public int top {Get; Set ;}} /// <summary> // obtain the data class from the document /// </Summary> public class articledatahelper {public static ienumerable <Article> getlist (articlequeryparameter parameter) {return NULL ;}}
Actually, it is to get the data source of articlelist. The specific implementation methods may be different, but article. list eventually requires such a method for obtaining data. However, this method must accept parameters of some query conditions. These parameters are from parameters !! Now let's fill in the parameter construction section of getdatasource.
Private ienumerable <Article> getdatasource () {VaR parameter = new articlequeryparameter (); // construct the query parameter. categoryid = parameters ["categoryid"]. converttoint (0); parameter. top = parameters ["TOP"]. converttoint (parameters ["pagesize"]. converttoint (0); var pageindex = parameters ["pageindex"]. converttoint (1); If (pageindex> 1) {parameter. pageparameter = new queryparameter {pageindex = pageindex, pagesize = parameter. top };} return articledatahelper. getlist (parameter );}
Parameters is the parametercollection of label, which can directly access specific parameter through the index. Convertors <t> (T defaultvalue) can convert the value of parameter to the T type. This is one of the places where parameter is used. In addition, we can see the specific HTML attribute of the field and the getvalue method, and getvalue accepts the current article object as a parameter (if the parameter is not accepted, how can we get the value of a field :).
The entire list process should be clear, get the data source, loop the data, replace all fields in each row, and finally return the spliced HTML. Of course, this is a list. If it is another tag, it may be another solution. For example, system. the include tag. Its job is to embed a user control (partialtemplate), so its processing logic is completely different from the List (he first obtains the template based on the value of the templateid parameter, then pass all the parameters of the template to all the labels in the template, and finally return the result after the template is replaced as the result, without loops ). Therefore, our specific control logic is quite different, but in the end it is to return the replaced HTML, but all the lists are different, nothing more than repeating different data sources. Therefore, we should abstract the list, extract the public part, and try to make each specific label clearer. How to abstract it? Check whether there are any public parts that can be extracted.
All lists may have pages, so listbase should have pageparameter, and all lists will go through the datasoruce loop. Therefore, listbase implements the datasource loop by default, but adds a method called getdatasource. This method is abstract and all lists must be implemented.
/// <Summary> /// cyclic tag base class /// </Summary> public abstract class listbase: Label {public queryparameter pageparameter {Get; set ;} public abstract ienumerable <dynamic> getdatasource (); Public override string getrenderhtml () {var datasource = getdatasource (); If (datasource = NULL) return string. empty; var html = templatestring; foreach (VAR dataitem in datasource) {foreach (VAR field in fields) {field. data = dataitem; html = html. replace (field. HTML, field. getvalue () ;}} return HTML ;}}
In foreach, I also made some minor adjustments, that is, I removed the getvalue parameter of field and changed it to a member, so that I can better understand it. You may have some questions:
Why is the Design Abstract instead of virtual methods or interfaces?
The implementation methods of all sub-classes are inconsistent, and no part can be extracted. Therefore, virtual functions are meaningless. If they are abstracted as interfaces separately, all sub-classes must inherit this interface, because getrenderhtml is closely integrated with this method, it is meaningless to explicitly convert it to an interface in foreach.
Why is the getdatasource method instead of making a datasource member public?What if set is required? Add another setdatasource?
In fact, I have considered that there are very few set cases, because the tags are automatically generated without external interference (SET), but there is no denying that they will not exist in the future. If they are set as members, then there must be a get-ready place, either abstract, that would also abstract the set, or give the set first in init, then there must be an abstract set method. Therefore, considering the status quo, it is most appropriate to use one method.
Another point is why dynamic is used instead of T.
First, it cannot be T. If it is t, you must specify t when calling getrenderhtml. Then, the entire listbase will become a generic listbase <t>, unless the base does not execute the getdatasource call. Why cannot I use listbase <t>? Because some getdatasource returns an anonymous type set with LINQ, The subclass cannot determine the specific type name returned, so it cannot inherit the listbase <t>. However, we can use dynamic and dynamic types to determine t in actual execution. We don't have to worry about this, but the object is obviously inferior.
In this way, the list of article only needs to implement getdatasource.
This is just the simplest form of list. What if I still need headtemplate itemtemplate foottemplate altertemplate spacetemplate like the Repeater control?
This requires defining the sub-label class. I will not talk about it here. In fact, it is very simple, that is, to define a few labels, and their respective methods for getting HTML, we can combine them at last. Note that the template and field of the list are all sub-tags. In addition, items and alter that are executed alternately need to be retained to assign values to each other. The following is the code I wrote earlier. Although it is ugly and not too Oo, it shows the implementation logic:
Code
today we are talking about this. I don't know if you are interested in it. We haven't demonstrated it yet, maybe one day I will release a demo source code. Next time, let's talk about the field design. This is also the last design.