The text before the first two sentences, distance from the previous blog has been nearly two months, this aspect of learning and exploration did not stop, but the way forward to meet a variety of problems, the need for continuous collation, reflection and optimization, the results of this period of time, will be in the recent finishing hair out.
The personal feeling of domestic mentality is too impetuous, few can go deep into the study and will own experience to share the person, may be very busy, may be too troublesome. Especially for the new technology, especially in the case of limited learning materials, willing to spend time to explore and share the people are too little too little, encounter problems, search results a catch a lot, but often are reproduced, even the least of their own verification is not, the result is baseless assertion, not only to solve the problem is useless, but easily misleading. Recently this time feeling quite deep, encountered problems often need to seriously consider choosing the appropriate English keywords from the English site to find solutions.
This piece of technology, I am also a novice, encountered more problems, I found that the current level and ability to write a logical relatively strong series is very difficult, so decided to start from the point, will find the problem-"Find a solution-" The final solution to the whole process of recording down, and finally a point string up, forming a line. As their own summary and reflection, but also for the latter to provide some experience and help, can take a few detours. At the same time, also left some unresolved problems, welcome to exchange, please correct me.
Well, into the text, the use of operamasks-ui2.0 +mvc4.0+ef5.0 this mode or architecture, the first to face is an unavoidable problem, the front and back of the exchange of data, the most popular and most efficient way is the JSON (up to a few years may be more XML, The pros and cons of JSON and XML, not described here, interested in their own information it. That is, the data is taken out of EF, this is the object, through the MVC scheduling, after serialization into JSON format, passed to the foreground, the foreground development framework (OPERAMASKS-UI or Easyui or ExtJS even jquery, etc.) after the reception for parsing and presentation. That's when the problem comes, how to serialize? If I hadn't done this development, I would have thought it would be easy, including the one I mentioned in my previous series, and I wrote an extension method for object:
Public Static string toJSONString ( this Object obj) { new JavaScriptSerializer (); New StringBuilder (); S.serialize (obj, SB); return sb. ToString (); }
And do the following with the controller.
PublicActionResult GetMenu () {IQueryable<Menu> Menu =Menuservice.query (); varnodes =NewList<treenode>(); foreach(varIteminchmenu. ToList ()) {TreeNode node=NewTreeNode (); Node.id=item.id; Node.pid=item. ParentID; Node.text=item. Name; Node.url=item. URL; node.expanded="true"; Nodes. ADD (node); } returnContent (nodes. toJSONString ()); }
In fact, the above approach is superfluous, completely unnecessary. It is easier to call the JSON (object) method directly, which is the return Content (nodes. toJSONString ()); Replace with return Json (nodes);. In fact, before I wrote the extension method to serialize, I used this method, the results found that the foreground is not receiving data, and then go to find data, using the JavaScriptSerializer class serialization object, using the return Content (serialized JSON) mode. As for why did not use, the reason also found, a small detail, is also a novice often made a mistake, in this also say. The problem is not the serialization of the object in JSON format, but the method of the HTTP protocol. Yes, you do not read wrong, is the HTTP protocol, the foreground through the Ajax call backstage method, must pay attention to is get or post, if it is get, then by default, the method in the controller after processing, call JSON (object) is forbidden get get, Instead, it has to call its overloaded function return Json (object, jsonrequestbehavior.allowget); This problem is very covert, do not know this matter, it will be inexplicable, obviously back to the background debugging data, but the front desk will not receive data. There is also an implicit mine is that you use some of the foreground framework of some of the controls, often only call its methods, and its interior is often through Ajax to load and refresh the data, do not pay attention to, will also occur, resulting in the front desk can not access data.
The above space is not small, actually only said a small problem, for the following to do a cushion.
Here's the core issue. Using EntityFramework, the Code first mode is used to define the entity classes, and the classes have normal and navigational properties. In the case of departments, this is a common self-correlating pattern, which is to define a field to point to its parent department, thus creating an infinitely hierarchical scale, which is obviously a one-to-many relationship where a department has only one parent department and possibly multiple subordinate departments. As shown below (simplified processing has been done and some extraneous attributes are not included).
Public classDepartment {[DisplayName ("Inner Code")] Public stringID {Get;Set; } [DisplayName ("Department Name")] Public stringName {Get;Set; } Public stringParentID {Get;Set; } [DisplayName ("Parent Department")] [ForeignKey ("ParentID")] Public VirtualDepartment parentdept {Get;Set; } Public VirtualIcollection<department> sondepts {Get;Set; }}
By using the ForeignKey property tag, you can implement a self-specified field name that maps to a database. If not used, EF can also be generated automatically, but in accordance with the Convention (class name +id) to write the property name, I still like their own site, automatic generation prone to indicate unclear, modify the error problem. As to foreignkey inside of the parameter how to write, just study time also puzzled for a while, this foreignkey should be marked on the attribute ParentID or mark on the navigation property parentdept? is the parameter inside the name of the library table that corresponds to the foreign key? Try, as if some of the wording of the prompt error, some of the hint is normal, suggesting that there is more than one way of writing. Later through the groping and check data, only know two methods can, one is I write above, mark in the Navigation property parentdept, the parameter is attribute ParentID, the other is the tag in the attribute ParentID, the inside parameter is the navigation property parentdept, namely
[ForeignKey ("Parentdept")]
public string ParentID {get; set;}
Public virtual Department parentdept {get; set;}
Now back to think that this is a natural definition of each other, but at the beginning of the time is really how to write the right to be troubled a while.
Go ahead and say the text. For a one-to-many relationship, the EF processing mode is to add a navigation property that indicates the ancestor, plus a navigation property that indicates the subordinate, as written above
Public virtual Department parentdept {get; set;}
Public virtual icollection<department> sondepts {get; set;}
I did it at the beginning, and it was done in some tutorials online. It can be said that this is not a problem in itself, but when serializing to a JSON object, there is a big problem and you get an error message: A circular reference is detected when serializing an object of type XX. When you see this error, you may feel puzzled, why do you report this error? In fact, the principle is very simple, when serializing, first find a department a, and then have a subordinate department of the Navigation properties, serialization of sub-department B when the parent department of the navigation properties point to the Department a, the result is a circular reference, with the death cycle a truth.
How to solve this problem? One solution is to use LINQ to entity, take the basic attributes out, discard the navigation properties, but this method is more restrictive, need to write the non-navigation attribute field of the entity class manually once, very cumbersome, poor versatility, and discard the reference attribute, the advantages of lazy loading is also lost.
So is there a better way to solve it?
I looked up the information from the Internet and some people said it would be db. Contextoptions.proxycreationenabled=false; some say it will be db. Contextoptions.lazyloadingenabled=false, some people say that for navigation properties plus [Scriptignore] tags, I actually tried, useless, I don't think the MVC version problem, but this is not the nature of the problem, The person who passed the information did not verify that it was really useful at all. In fact, as long as the navigation property has the virtual keyword, then the EF will delay loading, the serialization will occur when the circular reference, which is the nature of the problem. Since the nature of the problem is found, the solution is also very simple, remove the virtual keyword on the line. This is true and does not report a cyclic reference error when serializing, but the benefits of EF lazy loading are completely discarded when doing so. For example, if you have lazy loading, you can use Model=>model directly in the view. Parentdept.name to bind display the department name without writing any code. If there is no delay loading, then the model. The Parentdept object is null, and you need to pass Dbset <department> in the controller's method. The Find (ParentID) method finds and instantiates a Department object, and then passes the name to the foreground through the ViewBag dynamic object, you can imagine how cumbersome this is.
How can we eliminate the loop-referenced ray and preserve the advantages of lazy loading? Looking at a lot of data and experimenting with so-called solutions, finally, a workaround on a foreign web site is to define the behavior of serializing JSON so that it ignores reference objects (navigation properties), in the App_ of the MVC Web application The WebApiConfig.cs file of the start directory has an additional sentence at the end of the Register method, which requires only one sentence:
Config. formatters.jsonformatter.serializersettings.preservereferenceshandling= Newtonsoft.Json.PreserveReferencesHandling.Objects;
In fact, there is a config in the original word. Formatters.remove (config. Formatters.xmlformatter); This sentence has nothing to do with the question, do not add. Of course, foreigners also have baseless assertion people exist, said add CONFIG. formatters.jsonformatter.serializersettings.referenceloophandling= Newtonsoft.Json.ReferenceLoopHandling.Ignore , and CONFIG. Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize; These two methods appear to be valid, and the actual validation is not valid.
Add this sentence is not finished, you also need in the entity class, a one-to-many relationship in the case, the only one of the navigation properties reserved, the above department of the example, you keep parentdept can, keep sondepts also line, only one, EF can correctly recognize that this is a pair of, the library table generated the correct foreign key. If you still have two navigation properties left, then congratulations, you will still get a circular reference error.
Using the above method, you can add the virtual keyword to the navigation property, or you can preserve the benefits of EF lazy loading.
The article is not over yet, there is a tail. There is a common in the relationship, that is, many-to-many, such as roles and people, a role has multiple people, a person has multiple roles, according to the tutorial, should be in the person entity class add a navigation property public virtual icollection<role> Roles { Get Set }, add a navigation property to the role entity class public virtual icollection<user> Users {get; set;},ef will automatically recognize and generate an intermediate library table with only UserID and Roleid two fields. It all seems to be all right, and that's the usual way of dealing with it. However, when you serialize into JSON, you will encounter a circular reference to this ray, even as I have set up as above, still can not avoid, think, still is the cycle of death.
Then I came up with a way to define an intermediate entity, named Roleuser, and then, in the user entity class, add public virtual icollection<roleuser> roleusers {get; set;}, In the Role entity class, add public virtual icollection<roleuser> roleusers {get; set;}, that is, a many-to-many relationship turns into two one-to-many, which can be handled in the way above. Another advantage is that you can add additional fields to the intermediate relational mapping table, such as creation time, creator, and some necessary business information, such as adding a score field to a student's selection record.
The above is my own groping out of the solution, for the moment, is the best solution that I can think of, for everyone's reference, welcome criticism, to provide a better way to achieve.
There may not be a lot of people in EF, but hibernate should have similar problems, have no contact with hibernate, and welcome to learn how hibernate people are dealing with this.
April 5, 2013 supplementary amendment:
After the publication of this article, there are two friends leave a message to propose their own method, one is to use the Jsonignore property, the second is to use the DataContractJsonSerializer class, looks like useful, after testing, found or invalid, but also further provide ideas. In fact, after finishing the article, we found that the problem is actually very simple, that is, when serializing the object to deal with the problem of circular reference, as long as the serialization class to specify the behavior, then all the problems will be solved. In fact, I have been a bit insecure, is the method I originally figured out, in WebApiConfig.cs Riga to the JSON class control, whether really can play a role? Previously did not understand Webapi, and checked the information, found that with the normal control call is two different things, it means that no matter what is in the inside, should not be in the existing controller inside the method has influence. The direction is mistaken ... Then think about it, if you remove the modified code, then my program should also be able to run, so try it, sure enough normal ... As long as a one-to-many relationship only retains a navigation property, then naturally there is no circular reference problem, I think the correct solution is in repeated testing and many modifications resulting from the confusion of the illusion, and thus almost also become misleading people's murderer ...
The previous groping is not all useless work, the understanding of this piece has been deepened, but also has a new idea. The direction is clear, which is to specify the behavior for the serialization class to control the behavior at the time of serialization. So how to control it, preceded by the JavaScriptSerializer class for the object class to write the extension method, obviously there is no controllable properties, And before the groping also know Newtonsoft.Json.JsonConvert this class can control the behavior of cyclic application, try to change the previous extension method, sure enough.
have been suspected before the Controller.json method is called by the default internal call is the JavaScriptSerializer class to serialize the object, according to this idea searched, sure enough to find someone in-depth study, url see/ http/ Blog.darkthread.net/post-2012-08-30-asp-net-mvc-and-json-net.aspxis obtained by looking at the MVC source, And it provides how to inherit the default controller and overloaded Json methods, replacing the serialized JavaScriptSerializer with the Newtonsoft.Json.JsonConvert method, which seems to be logically no problem, specifically not verified. Considering this approach is troublesome, and later to upgrade the MVC version is prone to problems, or with my previous written extension method to solve the most convenient.
Increase the toJSONString method for Oject objects (note adding Newtonsoft.Json.dll references to items)
usingSystem;usingSystem.Text;usingSystem.Web.Script.Serialization;usingNewtonsoft.json;namespacecommon.extentions{ Public Static classobjectextentions { Public Static stringtoJSONString ( ThisObject obj) {jsonserializersettings jssettings=Newjsonserializersettings (); Jssettings.referenceloophandling=Referenceloophandling.ignore; returnjsonconvert.serializeobject (obj, jssettings); } }}
Then in the control, after acquiring the data object, return Content (data. toJSONString ());
When you do this, you can retain the lazy load feature by using two navigation properties in a one-to-many relationship and adding the virtual keyword.
The thought of many-to-many relationships can also be conventionally processed, but you want to add a field to the middle table, you have to use the idea of splitting, welcome to verify, welcome to correct.
Original address: http://www.cnblogs.com/seawaving/archive/2013/04/02/2996996.html
EntityFramework model has foreign keys, JSON hints Loop reference workaround