从阿里软件平台到TOP,一个好的应用和失败的运营-技术篇

上一篇中讲到了 快递吧 系统的开发历程,这里简单讲讲系统中涉及到的稍微有点价值的技术细节。

 

系统由服务器和客户端组成,服务器端是.net3.5开发的B/S程序,客户端最开始用的WPF,现在改为.net2.0的普通应用程序。

服务器端:

使用 ADO.NET Entity Framework 来做数据访问层

优点:这个十分的方便,节约了很多的时间,数据结构有变化,也能很快修改,至于性能,因为数据量不大,所以还不好说,几十万条数据中读取20条并显示到页面,一般不到1秒。

缺点:在设计器中删除一个表,XML中并不能完全删除,导致无法再把这个表添加进来,只能用XML编辑器打开edmx文件,删除多余的部分,才能重新添加;

        存储过程需要返回一个实体才能在以面向对象的方式访问(在DatabaseEntities的实例化对象中才会有这个方法),否则只能用EntityCommand去执行。

注意:有了LINQ,可以很方便的联合查询一些数据,甚至可以将内存里构建的LIST和数据库里面的表一起做联合查询,如:

代码
var notexists  =  from ships  in  shippingList.rsp.ship
                join userep 
in  db.ExpressInfo
                on ships.tid equals userep.TradeId into r
                from o 
in  r.DefaultIfEmpty()
                
where  o  ==   null

但是千万不要这样做,这会大大降低性能(估计会将数据库中所有数据读到内存,再做查询。) ,应该将内存中的数据组织好传给数据库,用存储过程来做查询。

 

使用WCF来做前后台数据交互

我本人很不习惯使用ASP.NET提供的那一堆服务器控件,以前也不知道MVC架构,所以一直页面都是纯HTML,提交数据都采用AJAX方式,这个系统里面使用的“启用了AJAX的WCF服务”做数据交互,十分的方便,性能上也还没有发现有多大的影响。前台脚本使用Jquery,最开始用了一个叫Jtemplates的插件来处理数据,很方便,但是性能很低,如果使用这个生成20条稍微复杂一点的表格数据,需要花费几秒的时间,而用拼字符串的形式几乎不耗时,所以只能弃用。

可能使用Jquery的人已经非常多了,如果还没有用过的建议学习一下,很简单,很强大。

 

动态编译程序

有的地方需要动态的编译一段程序来获取数据,这个还是比较简单的,代码如下:

代码
         private   string  Compiler( string  code)
        {
            CompilerParameters vCompilerParameters 
=   new  CompilerParameters();
            vCompilerParameters.GenerateExecutable 
=   false ;
            vCompilerParameters.GenerateInMemory 
=   true ;
            
string  vSource  =
            
" using System;\n using System.Text;\n "   +
            
" public class Temp\n "   +
            
" {\n "   +  code  +
            
" }\n " ;
            CompilerResults vCompilerResults 
=
            CodeDomProvider.CreateProvider(
" CSharp " ).CompileAssemblyFromSource(vCompilerParameters, vSource);
            
if  (vCompilerResults.Errors.HasErrors)
            {
                
return   "" ;
            }
            Assembly vAssembly 
=  vCompilerResults.CompiledAssembly;
            
object  vTemp  =  vAssembly.CreateInstance( " Temp " );
            MethodInfo vTest 
=  vTemp.GetType().GetMethod( " MethodName " );
            
return  vTest.Invoke(vTemp,  null ).ToString();
        }

 

 

动态加载、卸载程序域,以及调用该程序域的方法

上一篇中讲到,程序可以自动更新,这里的自动更新不是程序启动的时候去更新,而是运行中,不会中断运行,就像杀毒软件更新病毒库一样。

对于这个,我也只是知其然不知其所以然,就贴一下代码好了。

首先要定义一个接口:

     public   interface  IRemoteInterface
    {
        ReutrnValue Invoke(
string  param);
    }

 

 

新建一个工程,将需要在新的应用程序域里面执行的代码放在这个工程,生成单独的DLL

需要跨域调用的类要继承上面的接口,以及MarshalByRefObject类:

    [Serializable]
    
public   class  ExpressDeal : MarshalByRefObject, IRemoteInterface
    {
         
public  ReturnValue Invoke( string  param)
        {
            
return   "" ;
        }
    }

 

 

创建对象的类

代码
     ///   <summary>
    
///  Factory class to create objects exposing IRemoteInterface
    
///   </summary>
     public   class  RemoteLoaderFactory : MarshalByRefObject
    {
        
private   const  BindingFlags bfi  =  BindingFlags.Instance  |  BindingFlags.Public  |  BindingFlags.CreateInstance;

        
public  RemoteLoaderFactory() { }
        
///   <summary>  Factory method to create an instance of the type whose name is specified,
        
///  using the named assembly file and the constructor that best matches the specified parameters.  </summary>
        
///   <param name="assemblyFile">  The name of a file that contains an assembly where the type named typeName is sought.  </param>
        
///   <param name="typeName">  The name of the preferred type.  </param>
        
///   <param name="constructArgs">  An array of arguments that match in number, order, and type the parameters of the constructor to invoke, or null for default constructor.  </param>
        
///   <returns>  The return value is the created object represented as ILiveInterface.  </returns>
         public  IRemoteInterface Create( string  assemblyFile,  string  typeName,  object [] constructArgs)
        {
            
return  (IRemoteInterface)Activator.CreateInstanceFrom(
                assemblyFile, typeName, 
false , bfi,  null , constructArgs,
                
null null null ).Unwrap();
        }
    }

 

 

调用方法

代码
    public   static   class  ExpressDealFactory
    {
        
private   static  AppDomain _ExpressDealDomain  =   null ;
        
private   static  RemoteLoaderFactory _ExpressDealAssembly  =   null ;
        
private   static  IRemoteInterface _Instence  =   null

        
private   static   object  obj  =   new   object ();
        
private   static   readonly   string  filePath  =  Path.Combine(AppDomain.CurrentDomain.BaseDirectory,  " dll/StreamSea.Express.Deal.dll " ); 
        

        
public   static  ReturnValue GetExpressList( string  param)
        {
            ReturnValue r
=  Instence.Invoke(param);
            
return  r;
        }
        
public   static  AppDomain ExpressDealDomain
        {
            
get
            {
                
if  (_ExpressDealDomain  ==   null )
                {
                    
lock  (obj)
                    {
                        
if  (_ExpressDealDomain  ==   null )
                        {
                            AppDomainSetup objSetup 
=   new  AppDomainSetup();
                            objSetup.ApplicationName 
=   " ExpressDeal " ;
                            objSetup.ApplicationBase 
=  AppDomain.CurrentDomain.BaseDirectory;
                            objSetup.PrivateBinPath 
=  AppDomain.CurrentDomain.RelativeSearchPath  ??  AppDomain.CurrentDomain.BaseDirectory;
                            objSetup.CachePath 
=  AppDomain.CurrentDomain.BaseDirectory;
                            objSetup.ConfigurationFile 
=  AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
                            objSetup.ShadowCopyFiles 
=   " true " ;
                            Evidence baseEvidence 
=  AppDomain.CurrentDomain.Evidence;
                            Evidence evidence 
=   new  Evidence(baseEvidence);
                            evidence.AddHost(
new  System.Security.Policy.Zone(System.Security.SecurityZone.MyComputer));
                            _ExpressDealDomain 
=  AppDomain.CreateDomain( " ExpressDealDomain " , evidence, objSetup);
                        }
                    }
                }
                
return  _ExpressDealDomain;
            }
        }
        
public   static  RemoteLoaderFactory ExpressDealAssembly
        {
            
get
            {
                _ExpressDealAssembly 
=  ExpressDealDomain.CreateInstance( " StreamSea.Common " " StreamSea.Common.RemoteLoaderFactory " ).Unwrap()  as  RemoteLoaderFactory;
                
return  _ExpressDealAssembly;
            }
        }
        
public   static  IRemoteInterface Instence
        {
            
get
            {
                _Instence 
=  ExpressDealAssembly.Create(filePath,  " StreamSea.Express.Deal.ExpressDeal " null );
                
return  _Instence;
            }
        }

 

这样,我们就可以动态的加载一个应用程序域,然后执行里面的方法,需要更新的时候,先卸载掉这个域:

if (_ExpressDealDomain != null)
{
      AppDomain.Unload(_ExpressDealDomain);
}

替换dll/StreamSea.Express.Deal.dll这个文件,再加载执行,程序就更新完成了。

 

客户端程序:

客户端最开始使用的WPF做的,界面很漂亮,我没有仔细学习过这个,找了个示例自己修改了几下,先看看效果:

做的是一个托盘程序,自己做的菜单按钮:

从阿里软件平台到TOP,一个好的应用和失败的运营-技术篇

鼠标移动时按钮是有效果的:

查询淘宝发出快递的界面:

从阿里软件平台到TOP,一个好的应用和失败的运营-技术篇

查询其他的快递

从阿里软件平台到TOP,一个好的应用和失败的运营-技术篇

大家可能没看出来,我的操作系统是最丑的Windows 2003,所以能有这样的效果已经很不错了。

后台的C#代码和普通的程序没有什么区别,主要是界面的设计上有了很大的变化,由于学艺不精,就不乱讲了,说一说体会。

界面很炫,很酷,很拉风。

要安装.net3.5,近300M的东西,一般用户太难接受。

内存占用太多,一启动就好几十M,开几个窗口就上百。

内存不释放,窗体我都只能做成静态的,关闭就是隐藏掉,打开就是显示,而不能new一个,否则,内存只增不减。

做成静态的之后,会无规律的出现一个问题,程序的功能没有任何问题,但是界面没有任何反应,不是无响应那种,而是鼠标上去没有动画,界面上的所有控件不会被重绘,点击按钮,代码可以正常执行,但是就是不会有动画,文本框中输入文字,文字能够输入(用WPF性能分析工具能够看到),但是界面上不会变;最小化一下窗体,就会正常了。搞了很久,不知道什么原因,估计与内存回收有关。

 

客户端的部署是用ClickOnce做的,也挺有意思的,后面有空再写吧。写得不好,请多海涵。

你可能感兴趣的:(top)