Blogs

XPO

eXpress Persistent Objects
  • XPO and ASP.NET - Where to put your persistent classes

    If you have been working with XPO in ASP.NET applications, you may have noticed that the standard XPO Persistent Object X.X (and also the new Persistent Classes X.X) template is not available when the current project is an ASP.NET project. We recently re-evaluated the decision to leave the templates out from that location, and while we found the original reason to be invalid by now, there are still some things to be aware of when working with persistent classes in ASP.NET projects.

    Back in the days of beta testing for VS 2005, we first discovered a problem that appeared when persistent class hierarchies were used in ASP.NET projects. The reason for this issue was that XPO stores certain information about classes inside the database, among those the name of the assembly from which the class was originally read. A lot of the time this information is not used, but when persistent classes are derived from other persistent classes and instances of those derived classes are created and saved, XPO looks up the assembly name information and tries to demand-load the correct assembly.

    In ASP.NET 2.0/VS 2005, a project is built (by default) into several assemblies, and the names of these assemblies include a "key" part that changes regularly after rebuilding. So when XPO stores away the assembly name of a certain class in a first application run, then that information is likely going to be invalid on a later run because by now the name of the assembly has changed.

    When we discovered this issue originally, we found two workarounds. The first workaround is to enable a project level option in VS 2005, called Use fixed naming and single page assemblies (you can find it on the MSBuild Options page). While this makes sure that the assemblies have reliable static names, it has the major disadvantage of creating lots of little assemblies, because every single (web) page now gets its own. Well.

    The second workaround is still our current recommendation, which is to use a separate assembly for your persistent classes -- a standard "class library" project type will do for this. Not only did this solve the issue above, it is also generally a good practice because it allows you to easily create other applications that use the same classes as your web application. Think of admin frontends running on Windows Forms, database update utilities and so on...

    Now, in our recent re-evaluation we found something that was news to us: there is special magic implemented in the .NET framework, so that assemblies with a name that starts with App_Code will be found even if the rest of their name doesn't equal the search string. In other words, a call to Assembly.Load("App_Code.myRandomPostfix") will load the actual App_Code assembly when used from a web application, never mind the fact that the actual assembly obviously uses a different postfix. This magic handling seems to be in place for .NET 2.0 and above. We're not quite sure why we didn't see it earlier, but probably it was added before the final 2.0 release.

    This discovery means that technically your persistent classes are safe when stored inside the App_Code subfolder of your ASP.NET project. We will therefore make the templates available for use in those projects in the next minor update. I would still recommend you use a separate assembly, but at least we can get rid of a somewhat arbitrary restriction now.

  • XPO and ASP.NET - a look at the initialization code

    Mehul got back to me yesterday and asked me to explain the XPO related initialization code that is being used in our new ASPxGridView demo. This is the code in question:

        void Application_Start(object sender, EventArgs e) 
    {
    if(DemoSettings.IsSiteMode) {
    ConnectionStringSettings conn =
    ConfigurationManager.ConnectionStrings["XPOServerMode"];
    if(conn != null) {
    // Code that runs on application startup
    DevExpress.Xpo.Metadata.XPDictionary dict =
    new DevExpress.Xpo.Metadata.ReflectionDictionary();
    dict.GetDataStoreSchema(typeof(ServerSideGridTest).Assembly);
    DevExpress.Xpo.DB.IDataStore store =
    DevExpress.Xpo.XpoDefault.GetConnectionProvider(
    conn.ConnectionString,
    DevExpress.Xpo.DB.AutoCreateOption.SchemaAlreadyExists);
    store = new DevExpress.Xpo.DB.DataCacheNode(
    new DevExpress.Xpo.DB.DataCacheRoot(store));
    object layer = new DevExpress.Xpo.ThreadSafeDataLayer(dict, store);
    Application.Add("XpoLayer", layer);
    }
    }
    }

    Great. Now what's happening here? Let's go over the method line by line:

        if(DemoSettings.IsSiteMode) { 
    ConnectionStringSettings conn =
    ConfigurationManager.ConnectionStrings["XPOServerMode"];

    These are demo specific lines, nothing to do with XPO. In fact, when working with XPO it is a common practice to use helper methods on certain XPO classes to retrieve an auto-generated connection string instead of using a pre-created one. For SQL Server, for example, you'd want a call into DevExpress.Xpo.DB.MSSqlConnectionProvider.GetConnectionString( ... ); to get a connection string. There's nothing wrong with the approach of storing away the connection string, but it's certainly a little easier to break.

        DevExpress.Xpo.Metadata.XPDictionary dict = 
    new DevExpress.Xpo.Metadata.ReflectionDictionary();

    The dictionary of metadata that XPO works with contains important information about the persistent classes in your application, and it is associated with the so-called data layer (an object that implements IDataLayer, if you want to be technical about it). In general, a data layer is only created once per application, so the metadata dictionary is also created just once.

    In a Windows Forms application the code related to configuring the data layer is typically much simpler, probably something like this:

        XpoDefault.DataLayer = XpoDefault.GetDataLayer( ... );

    In ASP.NET, things are not quite as simple because we have to deal with the fact that we're working in a multi-threaded environment. If you take a little peak ahead in the complete code of the Application_Start method above, you will see a class called ThreadSafeDataLayer being instantiated. This specific implementation of IDataLayer can work in multi-threaded applications, but it requires a metadata dictionary that is stable during the application's lifetime. A data layer in a Windows Forms (single threaded) application would be extended during the application run, depending on the classes that are being used. This is not allowed to happen in the ASP.NET multi-threaded scenario, and that's why we have to create and fill the metadata dictionary manually, and before creating the data layer.

        dict.GetDataStoreSchema(typeof(ServerSideGridTest).Assembly); 

    This line does the work: it needs an assembly reference (or several actually, it is possible to pass in more than one assembly reference at once), finds all the metadata in that assembly and adds it to the dictionary. To get to the correct assembly reference, one of the persistent types in the application is used to find the reference dynamically. This is an arbitrary persistent type in your application -- it looks a bit a hack, but it's one of the easiest ways of getting hold of such a reference.

        DevExpress.Xpo.DB.IDataStore store = 
    DevExpress.Xpo.XpoDefault.GetConnectionProvider(
    conn.ConnectionString,
    DevExpress.Xpo.DB.AutoCreateOption.SchemaAlreadyExists);

    Now we are ready to create a connection provider object, which is an object that implements the interface IDataStore. This is basically the DB specific driver that XPO needs to communicate with your backend of choice. The XpoDefault.GetConnectionProvider( ... ) method analyzes the connection string to find out which type of connection provider it needs to create. For connection strings that are retrieved from the XPO helper methods (see above), there's a special flag inserted into the connection string that simplifies identification. In the ASPxGridView demo, the connection string is read from the Web.config file, but it still contains the flag. Here's the string (wrapped):

        XpoProvider=MSSqlServer;data source=(local);user id=servermodeprojects;
    password=smp$;initial catalog=ServerModeGridProjects;Persist Security Info=true

    The first parameter XpoProvider tells the GetConnectionString method that it needs to instantiate the connection provider for MS SQL Server in this case. If your connection string doesn't have such a flag, there are heuristics in that method trying to recognize the string nevertheless, but this may not work totally reliably. You should either retrieve the connection string from one of the helper methods or make sure the flag is in there if you store the complete connection string in a config file.

    Finally, there's an enum AutoCreateOption being passed into that method. This tells XPO how to handle the database schema - SchemaAlreadyExists says that the schema shouldn't be created, because it is expected to be there already. There are several other alternatives documented in the XPO product documentation.

        store = new DevExpress.Xpo.DB.DataCacheNode( 
    new DevExpress.Xpo.DB.DataCacheRoot(store));

    This line wraps the IDataStore in instances of DataCacheRoot and DataCacheNode. DataCacheNode implements IDataStore itself, so the result of the operation can be used anywhere a "normal" connection provider can also be used. The purpose of the wrapping is to establish data layer caching, as described in this technical article.

        object layer = new DevExpress.Xpo.ThreadSafeDataLayer(dict, store);

    With the metadata dictionary and the connection provider in hand, we can finally create our thread-safe data layer. I don't know why the person who wrote the demo used object as the type -- it should really be something explicit, must likely IDataLayer.

        Application.Add("XpoLayer", layer); 

    This line of code stores the data layer away for later use. Apart from the ASP.NET specific approach used here, there's also a mechanim built into XPO to set a default data layer: the static XpoDefault.DataLayer property. The main difference is that if XpoDefault.DataLayer has been set, it is no longer necessary to pass in an explicit data layer reference when constructing instances of Session or UnitOfWork. Of course there's the general argument that this works only as long as just one data layer is being used in an application, but that is really very common -- as I hear, the approach was chosen in this case becauses use of the Application class for this kind of thing is regarded as a good practice in ASP.NET applications. Wouldn't necessarily convince me, but there you go.

  • Receiving SqlDependency notifications for use outside XPO

     

    Well, there's obviously one really easy way to do it: just hook into the SqlDependency feature independently from XPO and receive the notifications that way. But there's an easier way to do it if XPO is hooking these notifications anyway. I got some code from an XPO developer and I created a sample around it to show how it works.

    Note: Before that developer agreed to give me his code, he made me promise to say this: we really don't recommend you use this! It is an approach that will have severe performance drawbacks in many multi-user scenarios. If you end up actually using it, please make sure you do testing on your application in circumstances that resemble production use. If you find that you have performance problems - which we believe is very likely -, we will not be able to do anything about it on the XPO level. Please consider yourself warned.

    Now you're probably asking: "why is he going to post a sample if he doesn't want me to use it?" Well, I believe that it is better to explain things to people, including (potentially) bad things, and let them make up their own minds. So here we go.

    The sample I'm using is the same I showed in the previous post about the SqlDependency feature. You can download the whole source code below. For the purpose of notification, the sample now includes a new class called DataStoreChangeTrackingWrapper, which has two important (and a bunch of not as important) methods:

    	...
    	protected void ProcessChanges(DataCacheResult result) {
    		CurrentCookie = result.Cookie;
    		if (result.UpdatedTableAges == null)
    			OnEverythingChanged( );
    		else if (result.UpdatedTableAges.Count > 0)
    			OnTablesChanged(result.UpdatedTableAges.Keys);
    	}
    	...
    	public void CatchUp( ) {
    		DataCacheResult result = Root.NotifyDirtyTables(CurrentCookie);
    		ProcessChanges(result);
    	}
    	...

    The DataStoreChangeTrackingWrapper class is a data store implementation. It overrides the ProcessUpdateSchema, ProcessSelectData and ProcessModifyData methods and makes sure that ProcessChange is called during the execution of all these methods. So if the application accesses the data store regularly (like the sample application does in its loop), the OnEverythingChanged and OnTablesChanged methods are going to be called automatically. The CatchUp method is for those cases where the regularity is not there - a timer could be used to call into CatchUp regularly to make sure that updates are passed through.

    The two methods OnEverythingChanged and OnTablesChanged are those that get called once changes have been detected in the database. OnTablesChanged is the more common of the two, and it gets more detailed information as well. OnEverythingChanged is called only in cases where the cache gets disconnected - for instance when the cache root service is stopped in the case of a remotely published cache root. 

    If you run the sample application and follow the same steps as in the previous article, you should see that at the point where external changes are being discovered, information about them will appear in the console. The sample uses a 5 second sleep in the main loop (previously one second), so that it's easy to spot the additional output line.

    Download the sample here: XPOSqlServer2005CacheAndNotification2.zip (4640 bytes)

  • XPO Beta feature: SqlDependency support

    SqlDependency is a SQL Server 2005 feature that allows the database server to notify a client when changes occur in the database. XPO already has a strong caching infrastructure, but up until now it didn't have support for the SqlDependency feature. We will introduce this support in our DXperience 7.2 release, and the recent 7.1.3 release includes the feature as a beta. Please go ahead and test it and let us know of any problems you may find, but don't rely on its stability until 7.2 is released.

    Prerequisites

    To make the SqlDependency feature work, there are some requirements that have to be kept. Microsoft has all the information about this on MSDN.

    A test program

    To demonstrate how the feature in XPO works, I have created a small test program. It starts out like this (I left out the Person class - it's a standard persistent class, please download the source from below if you need it):

    static void Main(string[] args) {
    	XpoDefault.DataLayer =
    		new SimpleDataLayer(new DataCacheNode(new DataCacheRoot(
    		XpoDefault.GetConnectionProvider(
    		MSSqlConnectionProvider.GetConnectionString(
    		"(local)", "XPOSqlServer2005CacheAndNotification"),
    		AutoCreateOption.DatabaseAndSchema))));
    
    	using (Session initSession = new Session( )) {
    		initSession.UpdateSchema(typeof(Person), typeof(XPObjectType));
    	}
    
    	for (; ; ) {
    		using (UnitOfWork uow = new UnitOfWork( )) {
    			Console.Clear( );
    			XPCollection people = new XPCollection(uow);
    			if (people.Count > 0)
    				foreach (Person p in people)
    					Console.WriteLine("Key: {0} Person: {1}", p.Oid, p.Name);
    			else
    				Console.WriteLine("Empty collection");
    			Thread.Sleep(1000);
    		}
    	}
    }
    

    As you can see, the program simply sets up a cached connection provider, creates all the table structures for the Person class, and then goes into a loop, querying again and again for all the Person instances in the database. Now, of course I am interested to see what queries actually go through to the database, so I also activate logging in the App.config (see here for the background on that):

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    	<system.diagnostics>
    		<switches>
    			<add name="XPO" value="3" />
    		</switches>
    	</system.diagnostics>
    </configuration>
    

    Now I run the application, and of course there's nothing in the database. The console shows "Empty collection" and the log output is also restricted to the minimum - note how the queries against the database are run just once, as the caching layer catches all the rest.

     

    Now, the interesting thing here is that when I go and add a record to the Person table in SQL Server Management Studio (while leaving the application running), the change will not be picked up by the application. So effectively the application is suddenly running with invalid data, although it's requerying all the time, and even using new sessions as well. This is the problem that the SqlDependency feature can solve.

     

    Using SqlDependency

    Using the SqlDependency support requires just one change to the source code of the test program: instead of calling the standard GetConnectionString method on the MSSqlConnectionProvider class, I use the method GetConnectionString2005WithCache instead. Because the connection provider that is created under these circumstances includes caching by default, I don't need to create the DataCacheNode and DataCacheRoot classes myself. So the line that sets the data layer looks like this:

    	XpoDefault.DataLayer =
    		new SimpleDataLayer(XpoDefault.GetConnectionProvider(
    			MSSqlConnectionProvider.GetConnectionString2005WithCache(
    			"(local)", "XPOSqlServer2005CacheAndNotification"),
    			AutoCreateOption.DatabaseAndSchema));
    

    Now when I rerun the application, I still get the caching behaviour, so the log output in VS looks much the same as before. The difference is that as soon as a record is added from SQL Server Management Studio, the change is picked up automatically. The next XPCollection that the test program creates results in an actual database query and the changed data is correctly retrieved. I can see in the VS log output how the query is executed - just once, as needed.

     

    Publishing the service

    It is important to consider that the SqlDependency feature uses resources on the server. Basically, every time a change happens in a table that is being monitored, every subscribed client must be notified. In conjunction with XPO, it seems advisable to make use of the publishing infrastructure, for instance via .NET Remoting, so that only one subscriber to the server's change notification exists. For this purpose there's another helper method on the MSSqlConnectionProvider class, called GetConnectionString2005CacheRoot. Using GetConnectionProvider with a connection string returned by GetConnectionString2005CacheRoot results in an object that implements ICachedDataStore. When this object is published and a client connects to it using a connection string of http://host:port/servicename or tcp://host:port/servicename format, XPO creates a DataCacheNode on the client automatically. The result is a setup that still benefits from change notifications on the database server, but doesn't have great resource requirements.

    As you have seen, the creation of objects that activate the SqlDependency feature is based on the connection strings. Incidentally, my own XPO publication service also uses connection strings in its configuration file, so it is compatible with this new feature. A connection string that would be returned by GetConnectionString2005CacheRoot would look like this, for example: XpoProvider=MSSqlServer2005CacheRoot;data source=(local);integrated security=SSPI;initial catalog=XPOSqlServer2005CacheAndNotification

    Download

    Here's the test program I created, if you want to try out everything for yourself: XPOSqlServer2005CacheAndNotification.zip (4109 bytes)

  • ANN: DXCore XPO plugin 1.1.0.5

    Here's the newest version - bug fixes, no new functionality. If you're not familiar with the purpose of the XPO plugin, please read this description of the "Simplified Criteria Syntax" feature.

    This is the download for version 1.1.0.5: CR_XPOFieldSync-1.1.0.5.zip (20432 bytes)

    Version 1.1.0.5 is compiled against DXCore 2.2.2, so it won't work with older versions. Here are the fixes:

    • Fixed CB44938 - wrong format of FieldsClass when deriving from generic class.
    • Fixed a problem where a property of the FieldsClass would get an invalid type if the corresponding property in the persistent class had a type that was a generic type parameter.
    • Fixed a bug where painting exceptions would be thrown while working in generic persistent classes.
    • Fixed CB46287 - XPCollection<T> as return type of properties breaks property generation in FieldsClass
    • Fixed CB46287 - Another problem with XPCollection as a property type
    • Fixed CB47358 - Persistent classes as property types lead to incomplete generation.

    The issue of CB8627 is still outstanding, as described previously. As usual, if you have any other problems with the plugin, please let us know!

  • ANN: DXCore XPO plugin 1.1.0.4

    Here's the newest version - bug fixes, no new functionality. If you're not familiar with the purpose of the XPO plugin, please read this description of the "Simplified Criteria Syntax" feature.

    This is the download for version 1.1.0.4: CR_XPOFieldSync-1.1.0.4.zip (20035 bytes)

    Here's what's been changed for this version:

    • Fixed two issues that came up when the immediate base class of a persistent class didn't have a FieldsClass, but another ancestor did. The issues were (a) that the FieldsClass in the persistent class would be removed and recreated with each change and (b) that the base class chosen for the FieldsClass was not correct.
    • Fixed an issue where the _fields field and the Fields property would apparently duplicate on each change.
    • Fixed an issue where FieldsClass properties were created for persistent class properties of non-persistent class types.

    The issue of CB8627 is still outstanding, as described previously. As usual, if you have any other problems with the plugin, please let us know!

  • ANN: DXCore XPO plugin 1.1.0.3

    So, apparently nobody's using the XPO plugin, I get it ... :-) I found a really bad bug in the recently released 1.1.0.2 build, which basically rendered it useless in many situations, and nobody told me about it! Ah well... now it's fixed, and the new build is here:

      CR_XPOFieldSync-1.1.0.3.zip (19770 bytes)

    Of course the same note of caution still holds: please make sure that this is the only version of the plugin you are using! I’ve seen a case in the past where there was more than one version of the plugin installed (several copies of the assembly in the plugin folder, with different names), and of course this results in some weird problems…

  • ANN: DXCore XPO plugin 1.1.0.2

    Here it is, another updated version of the XPO plugin. For some news that still apply to this update, please see my recent post. If you have no idea what this XPO plugin thing is about, you might also find this description of the "Simplified Criteria Syntax" feature useful.

    Here's the download for the newest version: CR_XPOFieldSync-1.1.0.2.zip (19764 bytes)

    Note: please make sure that this is the only version of the plugin you are using! I’ve seen a case in the past where there was more than one version of the plugin installed (several copies of the assembly in the plugin folder, with different names), and of course this results in some weird problems…

    News

    Here are the fixes in this version:

    • Fixed CB36365 - generic base classes are now handled correctly.
    • Fixed a problem where the base class of the FieldsClass wasn't changed correctly after the base class of the persistent class had been changed.
    • Implemented CS30989, in part. The important thing is that classes which derive from persistent classes will now get the strucures created even if they have the NonPersistent attribute applied, unless they are sealed.
    • The DefaultMembersPersistenceAttribute is now handled correctly.
    • The namespace DevExpress.Xpo.Helpers is now included in the file, in case the FieldsBaseClass is used as a FieldsClass ancestor.

    Outstanding issues

    • CB8627 – this bug is about the problem of incomplete structures. As I have previously reported in the issue comments, this is a problem without an easy solution at the current time, as the DXCore parser isn’t able to deliver the information I need. I have tried some workarounds, but without success so far – a complete solution of the issue may only come along with the introduction of a new parser in DXCore.

    Status

    Apart from the big known issue documented in CB8627, stability is pretty good as far as I know. Please let me know, preferably by way of the support center, of any issues you find.

  • New version of the DXCore XPO plugin - help me test!

    I've prepared a new version of the XPO plugin for DXCore. For those of you who haven’t used that plugin before – here’s the introduction of the “Simplified Criteria Syntax” feature. Once the new plugin version has been tested, I’ll have to make some changes to that page…

    Here’s the download of the new plugin version: CR_XPOFieldSync-1.1.0.1.zip (18330 bytes)

    Note: please make sure that this is the only version of the plugin you are using! I’ve seen a case in the past where there was more than one version of the plugin installed (several copies of the assembly in the plugin folder, with different names), and of course this results in some weird problems…

    News

    So what’s new in the new version? A whole bunch of things. See here, and read carefully:

    It is compiled for .NET 2. It will not run with .NET 1.x.
    Of course this means it can only be used in Visual Studio 2005.

    It creates code that is only compatible with XPO version 7.1 and later.
    The 7.1 RC that is currently available can be used with this plugin version, but no version earlier than that. My plan is to “backport” some of the bug fixes to a version of the plugin that will be the final version made available for .NET 1.1 compatibility (XPO 6.3 and older).

    It is compiled for DXCore 2.1.3.
    In my experience, compatibility with newer DXCore versions should be very good (although we’ve seen breaking changes in the past, if rarely), older versions will not work.

    There’s rudimentary logging support
    The plugin can log some information to the CodeRush Messages window. This can be helpful to track down bugs. The Messages window can be accessed from Visual Studio via the menu – DevExpress | Tool Windows | Diagnostics | Messages.
    The plugin has a new option “Logging active” that enables logging, it’s disabled by default.

    The structure of generated code has changed
    I have changed the structure of the code that the plugin generates. The reason for this is a suggestion submitted by a customer (thanks!), and the new structure allows using “long”, i.e. concatenated property names in queries (OrderLine.Fields.Order.Customer.CompanyName == “Developer Express”).
    The plugin has been changed to remove any old-format Fields class automatically and create the new structure instead.

    “new” and “Shadows” are now applied correctly (AB7485)

    “Persistent”, “NonPersistent” and “PersistentAlias” are interpreted correctly (CB18441)

    Outstanding issues

    Apart from ideas I have – of which there are quite a few –, there are also issues that are currently active in our support center, but haven’t been addressed in this release. I want to mention two such issues here:

    CS30989 – this is about the complex situation where classes in a hierarchy are partly persistent, partly non-persistent. It will require some thought to come up with a good implementation of this.

    CB8627 – this bug is about the problem of incomplete structures. As I have previously reported in the issue comments, this is a problem without an easy solution at the current time, as the DXCore parser isn’t able to deliver the information I need. I have some ideas for workarounds in this area and I’ll try to implement them – a complete solution of the issue may only come along with the introduction of a new parser in DXCore.

    Status

    I have done a lot of tests on this version, both in C# and VB.NET, but I’m absolutely sure there are more bugs in there that can be found. I’m planning to quickly fix any problems that are reported in the near future, and release another updated version soon. Of course this will work only if at least some of you download the current version and test it :-) I’d appreciate it if you did that and helped me with your bug reports – preferably please enter them in Support Center, so I can easily track them.

  • XPO 7.1 RTM - news about delayed properties, and a (potentially) breaking change

    Before you panic: this change is unlikely to actually break anything for the large majority of XPO users. Let me describe.

    Following a suggestion from a user, we implemented support for delayed properties in our simplified property syntax scheme. Looks like this:

      
      [Delayed]
      public Byte[] BigPicture {
        get { return GetDelayedPropertyValue<Byte[]>("BigPicture"); }
        set { SetDelayedPropertyValue<Byte[]>("BigPicture", value); }
      }
    

    As you can see, there’s no explicit storage variable used in this example. This is basically case (2) from my description of the variations of simplified property implementation. For delayed property implementation this is the recommended syntax from XPO 7.1 onwards – the one disadvantage outlined for case (2) was the absence of the local storage field. This is not a disadvantage for delayed properties, as the storage field itself (the one of type XPDelayedProperty) wasn’t any use for the programmer anyway.

    So what’s that about a breaking change?

    There are two important details that may change your application’s behaviour to some degree:

    1. Setting the delayed property value now fires an OnChanged event – a good thing, but a change nevertheless.
    2. For the change notification, the old value of the delayed property will be fetched from the database if it hadn’t been fetched previously.

    Note: This change is only in the final 7.1 version of XPO, not in the RC that is currently available.

« Previous page   Next page »
LIVE CHAT

Chat is one of the many ways you can contact members of the DevExpress Team.
We are available Monday-Friday between 7:30am and 4:30pm Pacific Time.

If you need additional product information, write to us at info@devexpress.com or call us at +1 (818) 844-3383

FOLLOW US

DevExpress engineers feature-complete Presentation Controls, IDE Productivity Tools, Business Application Frameworks, and Reporting Systems for Visual Studio, along with high-performance HTML JS Mobile Frameworks for developers targeting iOS, Android and Windows Phone. Whether using WPF, Silverlight, ASP.NET, WinForms, HTML5 or Windows 8, DevExpress tools help you build and deliver your best in the shortest time possible.

Copyright © 1998-2014 Developer Express Inc.
All trademarks or registered trademarks are property of their respective owners