I am using orientdb v2.1.4
Is it possible to do an orientdb traversal from a target record with a while condition and a maxdepth parameter in either SQL or the Java API? For example:
traverse all() from TargetRecordID maxdepth 25 while #class <> 'some_edge_class'
The orientdb parser seems to apply the maxdepth condition and ignores the while condition.
I use the while condition because I have 10 different edge classes overall, but I wish to do the traversal while excluding a subset (~3) of the edge classes. If you have an alternative approach that I use to ignore certain edge classes during the traversal, that would be great as well.
try this query:
traverse both() from YourClass while both('your_edge_class').size() = 0 and $depth <= 25
so the traverse won't pass on the vertices connected to the edge/edges that you specified
Edit:
Given the following graph example and assuming that you don't want the edges type1 and type2, would you like to get the nodes 1,2,3,4,5 and 8 connected to the edge type 3 or do you want to avoid all the vertices connected also with type 1and type 2?
Edit:
You can use this javascript function with three parameters (rid,maxDepth,excludeEdges)
var g=orient.getGraph();
var result=[];
var current=[];
var next=[];
var listEdges=excludeEdges.substring(1,excludeEdges.length-1).split(",");
var root=g.command('sql','select from '+rid);
current.push(root[0]);
var step=1;
while(current.length>0 && step<=maxDepth){
for(i=0;i<current.length;i++){
getVertex(current[i],"OUT");
getVertex(current[i],"IN");
}
change();
step++;
}
return result;
function change(){
current=[];
for (index=0;index<next.length;index++)
current.push(next[index]);
next=[];
}
function getVertex(start,direction){
var edgeDir="outE()";
var reverseDirection="in";
if(direction=="IN"){
edgeDir="inE()";
reverseDirection="out";
}
var edges=g.command("sql","select expand("+edgeDir +") from "+start.getId());
for(h=0;h<edges.length;h++){
var found=false;
for(m=0;m<listEdges.length;m++){
if(edges[h].getProperty("#class")==listEdges[m]){
found=true;
break;
}
}
if(found==false){
var vertex=g.command("sql","select expand("+ reverseDirection + ") from " +edges[h].getId());
for(j=0;j<result.length;j++){
if(result[j].getId().toString().equals(vertex[0].getId().toString()) ||
vertex[0].getId().toString().equals(rid)){
found=true;
break;
}
}
if(found==false){
result.push(vertex[0]);
next.push(vertex[0]);
}
}
}
}
Using the following command
select expand(result) from (select myFunction("#9:1",25,"[type1,type2]") as result)
With Java Api
OrientGraph g=new OrientGraph("remote:localhost/yourDb");
Traverse traverse = new Traverse(g);
List<String> listExcludeEdges=new ArrayList<String>();
listExcludeEdges.add("type1");
listExcludeEdges.add("type2");
int maxDepth=3;
String id="#9:1";
Set<Vertex> vertex=traverse.get(id,maxDepth,listExcludeEdges);
for(Vertex v:vertex){
System.out.println(v.getId());
}
g.shutdown();
Class traverse
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientGraph;
public class Traverse {
private Set<Vertex> listVertex = new LinkedHashSet<Vertex>();
private OrientGraph g;
private int maxDepth;
private List<String> listExcludeEdges;
private Vertex root;
public Traverse(OrientGraph g) {
this.g = g;
}
public Set<Vertex> get(String id,int maxDepth,List<String> listExcludeEdges) {
this.maxDepth=maxDepth;
this.listExcludeEdges=listExcludeEdges;
if(checkClassOrRid(id)){
if (getRoot(id))
getChildren(1,this.root);
}
return this.listVertex;
}
private boolean checkClassOrRid(String id){
Pattern pattern = Pattern.compile("^#[0-9]+:[0-9]+");
Matcher matcher = pattern.matcher(id);
if (matcher.matches()) {
for (Vertex v : g.getVertices()) {
if (v.getId().toString().equals(id))
return true;
}
}
return false;
}
protected boolean getRoot(String id) {
this.root = g.getVertex(id);
if(this.root!=null)
return true;
return false;
}
protected void getChildren(int step,Vertex from) {
if(step<=maxDepth){
getVertex(Direction.OUT,from,step);
getVertex(Direction.IN,from,step);
}
}
private void getVertex(Direction direction,Vertex from,int step){
Iterable<Edge> edges = from.getEdges(direction);
for(Edge e:edges){
boolean found=false;
for(String s:this.listExcludeEdges){
if(e.getProperty("#class").equals(s))
found=true;
}
if(found==false){
Vertex vertex=null;
if(direction==Direction.OUT)
vertex=e.getVertex(Direction.IN);
else
vertex=e.getVertex(Direction.OUT);
this.listVertex.add(vertex);
getChildren(step+1,vertex);
}
}
}
}
Related
public class BSTIterator {
private Stack<TreeNode> stk;
public BSTIterator(TreeNode root) {
stk = new Stack<>();
while (root != null) {
stk.push(root);
root = root.left;
}
}
/**
* #return whether we have a next smallest number
*/
public boolean hasNext() {
return !stk.isEmpty();
}
/**
* #return the next smallest number
*/
public int next() {
TreeNode curr = stk.pop();
int res = curr.val;
curr = curr.right;
while (curr != null) {
stk.push(curr);
curr = curr.left;
}
return res;
}
}
above is the java code snippet. how do i convert it to a scala class? I'm confused by the constructor part. how do i set up a constructor in this case?
var obj = new BSTIterator(root)
var param_1 = obj.next()
var param_2 = obj.hasNext()
The constructor parameters go on the class and can simply be used inside. You can also put statements directly in the body of the class and they become part of the constructor:
class BSTIterator(root: TreeNode) {
private val stk: Stack[TreeNode] = new Stack()
{
var root_ = root // you can't change parameters in Scala
while (root_ != null) {
stk.push(root_)
root_ = root_.left
}
}
...
}
The block is used so root_ doesn't become a field of the class. Also, you'd normally use Scala collections instead of Java Stack, but I'll leave that part to you.
I'm trying to make a game, using slick2d, and lwjgl. I don't get why this code doesn't work
firstStage.java
package net.CharlesDickenson;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
public class firstStage extends BasicGameState {
public bossVar bossChecker() {
if(isBeforeMiddleBoss) return bossVar.beforeBoss;
if(isMiddleBoss) return bossVar.Middle;
if(isBeforeBoss) return bossVar.beforeBoss;
if(isBoss) return bossVar.Boss;
return null;
}
#SuppressWarnings("static-access")
public firstStage(int state) {
this.state = state;
}
#Override
public void init(GameContainer _arg0, StateBasedGame _arg1)
throws SlickException {
scoreBoard = new Image("res/scoreBoard.png");
backs = new Image("res/1stageBack.gif");
isBeforeMiddleBoss = true;
isMiddleBoss = false;
isBeforeBoss = false;
isBoss = false;
_arg0.setShowFPS(false);
}
#Override
public void render(GameContainer arg0, StateBasedGame _arg1, Graphics _g)
throws SlickException {
this._g = _g;
new Mob().getGraphics(_g);//i passed graphics
new Char().getGraphics(_g);//i passed graphics
new Bullet().getGraphics(_g);//i passed graphics
_g.drawImage(scoreBoard, 550, 5);
_g.drawImage(backs, 10, 10);
_g.drawString(fps, 580, 570);
_g.drawString("High Score-> Not avaiable", 560, 60);
_g.drawString("Score-> " + currScore, 595, 80);
}
#Override
public void update(GameContainer _arg0, StateBasedGame _arg1, int arg2)
throws SlickException {
fps = "Frame Per Second-> " + _arg0.getFPS();
bossVar b = bossChecker();
switch(b) {
case beforeMiddle :
break;
case Boss :
break;
default:
break;
}
}
#SuppressWarnings("static-access")
#Override
public int getID() {
return this.state;
}
private static int state;
private static int currScore = 0;
private static final int originX = 270;
private static final int originY = 490;
public static int X = originX;
public static int Y = originY;
private static String fps;
private Image scoreBoard;
private Image backs;
private Graphics _g;
public boolean isBeforeMiddleBoss;
public boolean isMiddleBoss;
public boolean isBeforeBoss;
public boolean isBoss;
}
Char.java
package net.CharlesDickenson;
import org.lwjgl.input.Keyboard;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
public class Char extends Bullet implements Entity {
#Override
public void getGraphics(Graphics _g) {
this._g = _g;//so i got graphics, but
if(!isInit) return;
_g.drawImage(Char, getCharX(), getCharY());//this codes doesn't works.
}
#Override
public int getCharX() {
switch(VarTracker.stage) {
case 1:
return firstStage.X;
}
return 0;
}
#Override
public int getCharY() {
switch(VarTracker.stage) {
case 1:
return firstStage.Y;
}
return 0;
}
public void setCharX(int i) {
System.out.println("asdgagsd");
switch(VarTracker.stage) {
case 1:
firstStage.X += i;
}
}
public void setCharY(int i) {
System.out.println("asdgagsd");
switch(VarTracker.stage) {
case 1:
firstStage.Y += i;
}
}
#Override
public void update() {
if(!isInit) return;
_g.drawImage(Char, getCharX(), getCharY());//this code doesn't work, too.
up = Keyboard.isKeyDown(Keyboard.KEY_UP);
down = Keyboard.isKeyDown(Keyboard.KEY_DOWN);
left = Keyboard.isKeyDown(Keyboard.KEY_LEFT);
right = Keyboard.isKeyDown(Keyboard.KEY_RIGHT);
shift = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT);
z = Keyboard.isKeyDown(Keyboard.KEY_Z);
if(up && !shift) {
setCharY(6);
}
if(down && !shift) {
setCharY(-6);
}
if(left && !shift) {
setCharX(-6);
}
if(right && !shift) {
setCharX(6);
}
if(up && shift) {
setCharY(2);
}
if(down && shift) {
setCharY(-2);
}
if(left && shift) {
setCharX(-2);
}
if(right && shift) {
setCharX(2);
}
if(z) {
new Bullet().isFiring = true;
}
if(!z) {
new Bullet().isFiring = false;
}
}
#Override
public void init() {
System.out.println("<Char> Initializing...");
isInit = false;
try {
Char = new Image("res/char.png");
} catch (SlickException e) {
e.printStackTrace();
}
isInit = true;
System.out.println("<Char> Done with init()");
}
private boolean up;
private boolean down;
private boolean left;
private boolean right;
private boolean shift;
private boolean z;
private boolean isInit;
private Image Char;
private Graphics _g;
}
I passed graphics to other class using getGraphics method, to put a image, but it doesn't work.
at render method, it worked, but I can't put a image in other class.
The reason that it doesn't work is that you are using Graphics incorrectly. When Slick2d draws something, it uses the render method. This method is passed an instance of Graphics, to which you can draw stuff. When the call ends the Graphics object is no longer useful for anything. There is thus no reason to pass it to anything that doesn't use it immediately.
What you want to do is create a render method in your Mob, Char and Bullet classes. Make instances of said classes outside of the render method, for instance in init and store them in some data structure, for instance a List. In the render method, you simple traverse the list and call render or draw on each element. A quick example:
// Give the Entity interface two methods if they don't exist already:
public interface Entity {
void render(Graphics g);
void update(int delta);
}
// In firststage.java
List<Entity> list;
// In the init() method
public void init(GameContainer container, StateBasedGame game)
throws SlickException {
...
list = new ArrayList<Entity>();
list.add(new Mob());
list.add(new Char());
list.add(new Bullet());
}
// In the render method
public void render(GameContainer container, StateBasedGame game, Graphics g)
throws SlickException {
...
for (Entity e : list) {
e.draw(g);
}
}
// In the update method
public void update(GameContainer container, StateBasedGame game, int delta)
throws SlickException {
...
for (Entity e : list) {
e.update(delta);
}
}
TL;DR version: The Graphics object exists only to be drawn to in a single render call.
Render is called many times a second, so object creation in that method is not recommended.
Object oriented programming is good at modeling objects. Games tend to model a lot of objects. Make use of it.
I'm still a noob in Scala development but I have found the Option[T] concept really awesome, specially the pattern matching when used with Some and None. I am even implementing it so some extent in a C# project I'm working on at the moment, but as there is no pattern matching in there is isn't really that awesome.
The real question is, where is the theory behind this object? is it something specific from Scala? Funcional languages? Where can I find more about it?
Most of the time I was thinking that it comes from the Haskell, and has a name of Maybe monad
But after a little research, I've found that there was some references on option types in SML papers, as #ShiDoiSi said. Moreover, it has the same semantics (Some/None) that Scala has.
The elderest paper I was able to find is that (circa '89) (see footnote on the 6th page)
You don't need pattern-matching to use Option. I have written it in C# for you below. Note that the Fold function takes care of anything that would otherwise be pattern-matched.
Pattern-matching is generally discouraged in favour of higher-level combinators. For example, if your particular function can be written using Select you would use it rather than Fold (which is equivalent to pattern-matching). Otherwise, assuming side-effect free code (and therefore, equational reasoning), you would essentially be re-implementing existing code. This holds for all languages, not just Scala or C#.
using System;
using System.Collections;
using System.Collections.Generic;
namespace Example {
/// <summary>
/// An immutable list with a maximum length of 1.
/// </summary>
/// <typeparam name="A">The element type held by this homogenous structure.</typeparam>
/// <remarks>This data type is also used in place of a nullable type.</remarks>
public struct Option<A> : IEnumerable<A> {
private readonly bool e;
private readonly A a;
private Option(bool e, A a) {
this.e = e;
this.a = a;
}
public bool IsEmpty {
get {
return e;
}
}
public bool IsNotEmpty{
get {
return !e;
}
}
public X Fold<X>(Func<A, X> some, Func<X> empty) {
return IsEmpty ? empty() : some(a);
}
public void ForEach(Action<A> a) {
foreach(A x in this) {
a(x);
}
}
public Option<A> Where(Func<A, bool> p) {
var t = this;
return Fold(a => p(a) ? t : Empty, () => Empty);
}
public A ValueOr(Func<A> or) {
return IsEmpty ? or() : a;
}
public Option<A> OrElse(Func<Option<A>> o) {
return IsEmpty ? o() : this;
}
public bool All(Func<A, bool> f) {
return IsEmpty || f(a);
}
public bool Any(Func<A, bool> f) {
return !IsEmpty && f(a);
}
private A Value {
get {
if(e)
throw new Exception("Value on empty Option");
else
return a;
}
}
private class OptionEnumerator : IEnumerator<A> {
private bool z = true;
private readonly Option<A> o;
private Option<A> a;
internal OptionEnumerator(Option<A> o) {
this.o = o;
}
public void Dispose() {}
public void Reset() {
z = true;
}
public bool MoveNext() {
if(z) {
a = o;
z = false;
} else
a = Option<A>.Empty;
return !a.IsEmpty;
}
A IEnumerator<A>.Current {
get {
return o.Value;
}
}
public object Current {
get {
return o.Value;
}
}
}
private OptionEnumerator Enumerate() {
return new OptionEnumerator(this);
}
IEnumerator<A> IEnumerable<A>.GetEnumerator() {
return Enumerate();
}
IEnumerator IEnumerable.GetEnumerator() {
return Enumerate();
}
public static Option<A> Empty {
get {
return new Option<A>(true, default(A));
}
}
public static Option<A> Some(A t) {
return new Option<A>(false, t);
}
}
}
Wikipedia is your friend: http://en.wikipedia.org/wiki/Option_type
Unfortunately it doesn't give any dates, but I'd bet that it's ML-origin predates Haskell's Maybe.
I am currently examining Moles from the outside while I wait for my VS 2010 license, and I wonder whether Moles allows me to:
provide the ability to assÃgn multiple mole delegates for a method being moled, perhaps at a test fixture setup level?
switch in runtime in my test case, which of my mole delegates must be invoked for the upcoming call(s) to the moled method being isolated?
Any hints?
Best Answer:
It is much easier and makes far more sense to include gating logic in the detour method, than using two stubs for the same method! For example, MyMethod reads data from three different files on disk, each requiring different mock data to be returned. We may detour System.IO.File.OpenRead and gate the return value by analyzing the input parameters of OpenRead:
TEST METHOD:
[TestMethod]
[HostType("Moles")]
public void Test()
{
System.IO.Moles.MFile.OpenReadString = filePath => {
var mockStream = new System.IO.FileStream();
byte[] buffer;
switch (filePath)
{
case #"C:\DataFile.dat":
mockStream.Write(buffer, 0, 0); // Populate stream
break;
case #"C:\TextFile.txt":
mockStream.Write(buffer, 0, 0); // Populate stream
break;
case #"C:\LogFile.log":
mockStream.Write(buffer, 0, 0); // Populate stream
break;
}
return mockStream;
};
var target = new MyClass();
target.MyMethod();
}
TARGET TYPE:
using System.IO;
public class MyClass
{
public void MyMethod()
{
var fileAData = File.OpenRead(#"C:\DataFile.dat");
var fileBData = File.OpenRead(#"C:\TextFile.txt");
var fileCData = File.OpenRead(#"C:\LogFile.log");
}
}
Direct Answer to Your Questions:
Yes to #1: instantiate one type for each detour, and then use each for the desired behavior. And, yes to #2: act upon one instance of the mole type or the other. This requires addition of method input parameters or class constructor injection.
For example, MyMethod reads three data files from disk, and you need to pass back three different data mocks. MyMethod requires three parameters, an overtly intrusive solution. (Note input parameters are FileInfo type; because, System.IO>File is static and can not be instantiated: For example:
TEST METHOD:
[TestMethod]
[HostType("Moles")]
public void Test()
{
var fileInfoMoleA = new System.IO.Moles.MFileInfo();
fileInfoMoleA.OpenRead = () => { return new FileStream(); };
var fileInfoMoleB = new System.IO.Moles.MFileInfo();
fileInfoMoleB.OpenRead = () => { return new FileStream(); };
var fileInfoMoleC = new System.IO.Moles.MFileInfo();
fileInfoMoleC.OpenRead = () => { return new FileStream(); };
var target = new MyClass();
target.MyMethod(fileInfoMoleA, fileInfoMoleB, fileInfoMoleC);
}
TARGET TYPE:
using System.IO;
public class MyClass
{
// Input parameters are FileInfo type; because, System.IO.File
// is a static class, and can not be instantiated.
public void MyMethod(FileInfo fileInfoA, FileInfo fileInfoB, FileInfo fileInfoC)
{
var fileAData = fileInfoA.OpenRead();
var fileBData = fileInfoB.OpenRead();
var fileCData = fileInfoC.OpenRead();
}
}
UPDATE:
In response to #Chai comment, it is possible to create common methods, within the test project, that may be referenced as the mole detour delegate. For example, you may wish to write a common method that may be referenced by any unit test, that sets up a variety of pre-configured scenarios. The following example displays how a parameterized method could be used. Get creative -- they're just method calls!
TARGET TYPES:
namespace PexMoleDemo
{
public class MyClass
{
private MyMath _math;
public MyClass()
{
_math = new MyMath() { left = 1m, right = 2m };
}
public decimal GetResults()
{
return _math.Divide();
}
}
public class MyOtherClass
{
private MyMath _math;
public MyOtherClass()
{
_math = new MyMath() { left = 100m, right = 200m };
}
public decimal Divide()
{
return _math.Divide();
}
}
public class MyMath
{
public decimal left { get; set; }
public decimal right { get; set; }
public decimal Divide()
{
return left / right;
}
}
}
TEST METHODS:
ArrangeScenarios() sets up mole detours, by switching on the enumeration parameter. This allows the same scenarios to be erected, in a DRY manner, throughout many tests.
using System;
using Microsoft.Moles.Framework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PexMoleDemo;
[assembly: MoledAssembly("PexMoleDemo")]
namespace TestProject1
{
[TestClass()]
public class ProgramTest
{
public enum Scenarios
{
DivideByZero,
MultiplyInsteadOfDivide
}
private void ArrangeScenario(Scenarios scenario)
{
switch (scenario)
{
case Scenarios.DivideByZero:
PexMoleDemo.Moles.MMyMath.AllInstances.rightGet =
instance => { return 0m; };
break;
case Scenarios.MultiplyInsteadOfDivide:
PexMoleDemo.Moles.MMyMath.AllInstances.Divide =
instance => { return instance.left * instance.right; };
break;
default:
throw new NotImplementedException("Invalid scenario.");
}
}
[TestMethod]
[HostType("Moles")]
[ExpectedException(typeof(DivideByZeroException))]
public void Test1()
{
ArrangeScenario(Scenarios.DivideByZero);
var target = new PexMoleDemo.MyClass();
var math = new PexMoleDemo.MyMath() { left = 1, right = 2 };
var left = math.left;
var right = math.right;
var actual = target.GetResults();
}
[TestMethod]
[HostType("Moles")]
public void Test2()
{
ArrangeScenario(Scenarios.MultiplyInsteadOfDivide);
// Perform some sort of test that determines if code breaks
// when values are multiplied instead of divided.
}
[TestMethod]
[HostType("Moles")]
[ExpectedException(typeof(DivideByZeroException))]
public void Test3()
{
ArrangeScenario(Scenarios.DivideByZero);
var target = new PexMoleDemo.MyOtherClass();
var math = new PexMoleDemo.MyMath() { left = 1, right = 2 };
var left = math.left;
var right = math.right;
var actual = target.Divide();
}
[TestMethod]
[HostType("Moles")]
public void Test4()
{
ArrangeScenario(Scenarios.MultiplyInsteadOfDivide);
// Perform some sort of test that determines if code breaks
// when values are multiplied instead of divided.
}
}
}
Like several other people, I'm having problems serializing Entity Framework objects, so that I can send the data over AJAX in a JSON format.
I've got the following server-side method, which I'm attempting to call using AJAX through jQuery
[WebMethod]
public static IEnumerable<Message> GetAllMessages(int officerId)
{
SIBSv2Entities db = new SIBSv2Entities();
return (from m in db.MessageRecipients
where m.OfficerId == officerId
select m.Message).AsEnumerable<Message>();
}
Calling this via AJAX results in this error:
A circular reference was detected while serializing an object of type \u0027System.Data.Metadata.Edm.AssociationType
Which is because of the way the Entity Framework creates circular references to keep all the objects related and accessible server side.
I came across the following code from (http://hellowebapps.com/2010-09-26/producing-json-from-entity-framework-4-0-generated-classes/) which claims to get around this problem by capping the maximum depth for references. I've added the code below, because I had to tweak it slightly to get it work (All angled brackets are missing from the code on the website)
using System.Web.Script.Serialization;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System;
public class EFObjectConverter : JavaScriptConverter
{
private int _currentDepth = 1;
private readonly int _maxDepth = 2;
private readonly List<int> _processedObjects = new List<int>();
private readonly Type[] _builtInTypes = new[]{
typeof(bool),
typeof(byte),
typeof(sbyte),
typeof(char),
typeof(decimal),
typeof(double),
typeof(float),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(short),
typeof(ushort),
typeof(string),
typeof(DateTime),
typeof(Guid)
};
public EFObjectConverter( int maxDepth = 2,
EFObjectConverter parent = null)
{
_maxDepth = maxDepth;
if (parent != null)
{
_currentDepth += parent._currentDepth;
}
}
public override object Deserialize( IDictionary<string,object> dictionary, Type type, JavaScriptSerializer serializer)
{
return null;
}
public override IDictionary<string,object> Serialize(object obj, JavaScriptSerializer serializer)
{
_processedObjects.Add(obj.GetHashCode());
Type type = obj.GetType();
var properties = from p in type.GetProperties()
where p.CanWrite &&
p.CanWrite &&
_builtInTypes.Contains(p.PropertyType)
select p;
var result = properties.ToDictionary(
property => property.Name,
property => (Object)(property.GetValue(obj, null)
== null
? ""
: property.GetValue(obj, null).ToString().Trim())
);
if (_maxDepth >= _currentDepth)
{
var complexProperties = from p in type.GetProperties()
where p.CanWrite &&
p.CanRead &&
!_builtInTypes.Contains(p.PropertyType) &&
!_processedObjects.Contains(p.GetValue(obj, null)
== null
? 0
: p.GetValue(obj, null).GetHashCode())
select p;
foreach (var property in complexProperties)
{
var js = new JavaScriptSerializer();
js.RegisterConverters(new List<JavaScriptConverter> { new EFObjectConverter(_maxDepth - _currentDepth, this) });
result.Add(property.Name, js.Serialize(property.GetValue(obj, null)));
}
}
return result;
}
public override IEnumerable<System.Type> SupportedTypes
{
get
{
return GetType().Assembly.GetTypes();
}
}
}
However even when using that code, in the following way:
var js = new System.Web.Script.Serialization.JavaScriptSerializer();
js.RegisterConverters(new List<System.Web.Script.Serialization.JavaScriptConverter> { new EFObjectConverter(2) });
return js.Serialize(messages);
I'm still seeing the A circular reference was detected... exception being thrown!
I solved these issues with the following classes:
public class EFJavaScriptSerializer : JavaScriptSerializer
{
public EFJavaScriptSerializer()
{
RegisterConverters(new List<JavaScriptConverter>{new EFJavaScriptConverter()});
}
}
and
public class EFJavaScriptConverter : JavaScriptConverter
{
private int _currentDepth = 1;
private readonly int _maxDepth = 1;
private readonly List<object> _processedObjects = new List<object>();
private readonly Type[] _builtInTypes = new[]
{
typeof(int?),
typeof(double?),
typeof(bool?),
typeof(bool),
typeof(byte),
typeof(sbyte),
typeof(char),
typeof(decimal),
typeof(double),
typeof(float),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(short),
typeof(ushort),
typeof(string),
typeof(DateTime),
typeof(DateTime?),
typeof(Guid)
};
public EFJavaScriptConverter() : this(1, null) { }
public EFJavaScriptConverter(int maxDepth = 1, EFJavaScriptConverter parent = null)
{
_maxDepth = maxDepth;
if (parent != null)
{
_currentDepth += parent._currentDepth;
}
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
return null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
_processedObjects.Add(obj.GetHashCode());
var type = obj.GetType();
var properties = from p in type.GetProperties()
where p.CanRead && p.GetIndexParameters().Count() == 0 &&
_builtInTypes.Contains(p.PropertyType)
select p;
var result = properties.ToDictionary(
p => p.Name,
p => (Object)TryGetStringValue(p, obj));
if (_maxDepth >= _currentDepth)
{
var complexProperties = from p in type.GetProperties()
where p.CanRead &&
p.GetIndexParameters().Count() == 0 &&
!_builtInTypes.Contains(p.PropertyType) &&
p.Name != "RelationshipManager" &&
!AllreadyAdded(p, obj)
select p;
foreach (var property in complexProperties)
{
var complexValue = TryGetValue(property, obj);
if(complexValue != null)
{
var js = new EFJavaScriptConverter(_maxDepth - _currentDepth, this);
result.Add(property.Name, js.Serialize(complexValue, new EFJavaScriptSerializer()));
}
}
}
return result;
}
private bool AllreadyAdded(PropertyInfo p, object obj)
{
var val = TryGetValue(p, obj);
return _processedObjects.Contains(val == null ? 0 : val.GetHashCode());
}
private static object TryGetValue(PropertyInfo p, object obj)
{
var parameters = p.GetIndexParameters();
if (parameters.Length == 0)
{
return p.GetValue(obj, null);
}
else
{
//cant serialize these
return null;
}
}
private static object TryGetStringValue(PropertyInfo p, object obj)
{
if (p.GetIndexParameters().Length == 0)
{
var val = p.GetValue(obj, null);
return val;
}
else
{
return string.Empty;
}
}
public override IEnumerable<Type> SupportedTypes
{
get
{
var types = new List<Type>();
//ef types
types.AddRange(Assembly.GetAssembly(typeof(DbContext)).GetTypes());
//model types
types.AddRange(Assembly.GetAssembly(typeof(BaseViewModel)).GetTypes());
return types;
}
}
}
You can now safely make a call like new EFJavaScriptSerializer().Serialize(obj)
Update : since version Telerik v1.3+ you can now override the GridActionAttribute.CreateActionResult method and hence you can easily integrate this Serializer into specific controller methods by applying your custom [GridAction] attribute:
[Grid]
public ActionResult _GetOrders(int id)
{
return new GridModel(Service.GetOrders(id));
}
and
public class GridAttribute : GridActionAttribute, IActionFilter
{
/// <summary>
/// Determines the depth that the serializer will traverse
/// </summary>
public int SerializationDepth { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="GridActionAttribute"/> class.
/// </summary>
public GridAttribute()
: base()
{
ActionParameterName = "command";
SerializationDepth = 1;
}
protected override ActionResult CreateActionResult(object model)
{
return new EFJsonResult
{
Data = model,
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
MaxSerializationDepth = SerializationDepth
};
}
}
and finally..
public class EFJsonResult : JsonResult
{
const string JsonRequest_GetNotAllowed = "This request has been blocked because sensitive information could be disclosed to third party web sites when this is used in a GET request. To allow GET requests, set JsonRequestBehavior to AllowGet.";
public EFJsonResult()
{
MaxJsonLength = 1024000000;
RecursionLimit = 10;
MaxSerializationDepth = 1;
}
public int MaxJsonLength { get; set; }
public int RecursionLimit { get; set; }
public int MaxSerializationDepth { get; set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
if (JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
String.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException(JsonRequest_GetNotAllowed);
}
var response = context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
{
response.ContentType = ContentType;
}
else
{
response.ContentType = "application/json";
}
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
var serializer = new JavaScriptSerializer
{
MaxJsonLength = MaxJsonLength,
RecursionLimit = RecursionLimit
};
serializer.RegisterConverters(new List<JavaScriptConverter> { new EFJsonConverter(MaxSerializationDepth) });
response.Write(serializer.Serialize(Data));
}
}
You can also detach the object from the context and it will remove the navigation properties so that it can be serialized. For my data repository classes that are used with Json i use something like this.
public DataModel.Page GetPage(Guid idPage, bool detach = false)
{
var results = from p in DataContext.Pages
where p.idPage == idPage
select p;
if (results.Count() == 0)
return null;
else
{
var result = results.First();
if (detach)
DataContext.Detach(result);
return result;
}
}
By default the returned object will have all of the complex/navigation properties, but by setting detach = true it will remove those properties and return the base object only. For a list of objects the implementation looks like this
public List<DataModel.Page> GetPageList(Guid idSite, bool detach = false)
{
var results = from p in DataContext.Pages
where p.idSite == idSite
select p;
if (results.Count() > 0)
{
if (detach)
{
List<DataModel.Page> retValue = new List<DataModel.Page>();
foreach (var result in results)
{
DataContext.Detach(result);
retValue.Add(result);
}
return retValue;
}
else
return results.ToList();
}
else
return new List<DataModel.Page>();
}
I have just successfully tested this code.
It may be that in your case your Message object is in a different assembly? The overriden Property SupportedTypes is returning everything ONLY in its own Assembly so when serialize is called the JavaScriptSerializer defaults to the standard JavaScriptConverter.
You should be able to verify this debugging.
Your error occured due to some "Reference" classes generated by EF for some entities with 1:1 relations and that the JavaScriptSerializer failed to serialize.
I've used a workaround by adding a new condition :
!p.Name.EndsWith("Reference")
The code to get the complex properties looks like this :
var complexProperties = from p in type.GetProperties()
where p.CanWrite &&
p.CanRead &&
!p.Name.EndsWith("Reference") &&
!_builtInTypes.Contains(p.PropertyType) &&
!_processedObjects.Contains(p.GetValue(obj, null)
== null
? 0
: p.GetValue(obj, null).GetHashCode())
select p;
Hope this help you.
I had a similar problem with pushing my view via Ajax to UI components.
I also found and tried to use that code sample you provided. Some problems I had with that code:
SupportedTypes wasn't grabbing the types I needed, so the converter wasn't being called
If the maximum depth is hit, the serialization would be truncated
It threw out any other converters I had on the existing serializer by creating its own new JavaScriptSerializer
Here are the fixes I implemented for those issues:
Reusing the same serializer
I simply reused the existing serializer that is passed into Serialize to solve this problem. This broke the depth hack though.
Truncating on already-visited, rather than on depth
Instead of truncating on depth, I created a HashSet<object> of already seen instances (with a custom IEqualityComparer that checked reference equality). I simply didn't recurse if I found an instance I'd already seen. This is the same detection mechanism built into the JavaScriptSerializer itself, so worked quite well.
The only problem with this solution is that the serialization output isn't very deterministic. The order of truncation is strongly dependent on the order that reflections finds the properties. You could solve this (with a perf hit) by sorting before recursing.
SupportedTypes needed the right types
My JavaScriptConverter couldn't live in the same assembly as my model. If you plan to reuse this converter code, you'll probably run into the same problem.
To solve this I had to pre-traverse the object tree, keeping a HashSet<Type> of already seen types (to avoid my own infinite recursion), and pass that to the JavaScriptConverter before registering it.
Looking back on my solution, I would now use code generation templates to create a list of the entity types. This would be much more foolproof (it uses simple iteration), and have much better perf since it would produce a list at compile time. I'd still pass this to the converter so it could be reused between models.
My final solution
I threw out that code and tried again :)
I simply wrote code to project onto new types ("ViewModel" types - in your case, it would be service contract types) before doing my serialization. The intention of my code was made more explicit, it allowed me to serialize just the data I wanted, and it didn't have the potential of slipping in queries on accident (e.g. serializing my whole DB).
My types were fairly simple, and I didn't need most of them for my view. I might look into AutoMapper to do some of this projection in the future.