• AutoCAD 命令统计魔幻球的实现过程(1)


    前面的文章介绍了AutoCAD 命令统计魔幻球,大家有空可以玩玩看。今天开始介绍一下这个项目的实现过程。这个项目还是很简单的,其中用到的技术主要包括AutoCAD .net API, ASP.net MVC, Windows Azure, Entity Framework代码优先编程,RESTSharp, Jquery 和WebGL(Three.Js)。我首先介绍一下服务端的实现方法。

    image

    这个项目包括3个部分, – 一个AutoCAD插件来收集AutoCAD命令的使用情况上传到云端的服务程序, 一个驻留在Windows Azure云端的REST服务程序来接收/发送和存储命令统计信息, 另外是一个web页面中由JQuery以REST的方式从云端服务程序获取统计数据,并用WebGL渲染出来,这里使用了一个比较流行的类库Three.JS. 下面是Visual Studio的解决方案,包括5个项目。其中AcadCommandViewer是服务程序和网页浏览端,这是一个ASP.NET MVC应用,最后要部署到Windows Azure。另外还有个AutoCAD.net的插件程序。另外为了使数据模型能够重用,我把它独立出来成为一个单独的项目。

    image

    下面看一下服务端程序的实现。说到创建Windows Azure云端应用,你估计首先想到的就是用Windows Azure SDK的模版来创建,但是我发现这样是调试过程中在Azure 模拟器中测试还是挺慢的。其实把一个现有的asp.net 程序移植到到Windows Azure也还是很简单的,既然如此,那何不就从一个简单的ASP.NET程序开始呢? 于是我就在Visual Studio中创建了一个ASP.net MVC 4 Web Application 。

    首先需要定义数据模型,如果项目不大,在MVC项目的Models目录下定义就好了,但上面我也提到了,这个数据模型我还是要在AutoCAD插件项目中重用的,所以把数据模型独立出来成为一个单独的项目。很简单就是添加一个class library项目,加入一个数据模型的类即可:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using
    System.Web;

    namespace
    AcadCommandViewer.Models
    {
       
    public class UserCommandsHit
        {
           
    public int Id { get; set
    ; }
           
    public string UserName { get; set
    ; }
           
    public virtual ICollection<CommandHit> CommandHits { get; set
    ; }

        }

       
    public class CommandHit
        {
           
    public int Id { get; set
    ; }
           
    public string CommandName { get; set
    ; }
           
    public int HitNumber { get; set; }
        }
    }

    数据模型建好了,然后可以创建控制器了。Visual Studio提供了很好的模版,我使用Web API + Entity framework,这个模版其实已经帮我做了大部分的工作。在Controllers目录上点右键,添加--控制器,选择“API controller with read/write actions, using Entity Framework“模版,并选择上面创建的数据模型类,

    image

    然后选择“<New data context>”让Visual Studio来帮我们自动创建数据上下文类。如果你在上面的模型类列表中找不到你的数据模型类,那是因为你没添加引用,因为我的数据模型已经独立到另外一个项目中了,很简单,添加引用即可:

    image

    Visual Studio自动生成的控制器代码已经相当不错了,通过Web API实现了REST服务的GET, POST ,PUT 和DELETE方法,不过在PUT方法更新时有一点问题,就是我们上面的数据模型定义中是有父子关系的,默认的代码Entity Framework不能处理这个父子关系的模型,需要稍微改一点。解决办法就是把旧记录的子对象都删除,然后在加入新对象的子对象,具体可以看一下下面的代码及注释。

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Web;
    using System.Web.Http;
    using
    AcadCommandViewer.Models;

    namespace
    AcadCommandViewer.Controllers
    {
       
    public class AcadCommandsController : ApiController
        {
           
    private AcadCommandViewerContext db = new AcadCommandViewerContext
    ();

           
    // GET api/AcadCommands
            public IEnumerable<UserCommandsHit
    > GetCommandsDatas()
            {
               
    return
    db.UserCommandsHits.OrderBy(p => p.UserName)
                    .AsEnumerable();
            }

           
    // GET api/AcadCommands/5
            public UserCommandsHit GetCommandsData(int
    id)
            {
               
    UserCommandsHit
    commandsdata = db.UserCommandsHits.Find(id);
               
    if (commandsdata == null
    )
                {
                   
    throw new HttpResponseException
    (
                        Request.CreateResponse(
    HttpStatusCode
    .NotFound));
                }

               
    return
    commandsdata;
            }

           
    // GET api/AcadCommands?username=Daniel
            public UserCommandsHit GetCommandsData(string
    userName)
            {
               
    UserCommandsHit
    commandsdata = db.UserCommandsHits
                    .FirstOrDefault<
    UserCommandsHit
    >(p =>
                        p.UserName.ToUpper() == userName.ToUpper());
               
    if (commandsdata == null
    )
                {
                   
    throw new HttpResponseException
    (
                        Request.CreateResponse(
    HttpStatusCode
    .NotFound));
                }

               
    return
    commandsdata;
            }

           
    // PUT api/AcadCommands/5
            public HttpResponseMessage PutCommandsData(int
    id,
                               
    UserCommandsHit
    commandsdata)
            {
               
    if
    (ModelState.IsValid && id == commandsdata.Id)
                {
                   
    var
    usrCmdHitInDb = db.UserCommandsHits.Find(id);
                   
    //http://stackoverflow.com/questions/7968598/entity-4-1-updating-an-existing-parent-entity-with-new-child-entities
                    //SetValues never updates navigation properties.
                    //EF doesn't have any magic to update the children
                    // - which means: adding new children, deleting
                    // removed children, updating existing children
                    // this procedure forces you to delete the old
                    // children also from the database and insert the new one
                    db.Entry(usrCmdHitInDb).CurrentValues.SetValues(commandsdata);
                   
    //workaroud is to remove
                    foreach (var ch in
    usrCmdHitInDb.CommandHits.ToList())
                    {
                        usrCmdHitInDb.CommandHits.Remove(ch);
                    }

                    usrCmdHitInDb.CommandHits.Clear();

                   
    foreach (var item in
    commandsdata.CommandHits)
                    {
                        usrCmdHitInDb.CommandHits.Add(item);
                    }

                   
    try
                    {
                        db.SaveChanges();
                    }
                   
    catch (DbUpdateConcurrencyException
    )
                    {
                       
    return Request.CreateResponse(HttpStatusCode
    .NotFound);
                    }

                   
    return Request.CreateResponse(HttpStatusCode
    .OK);
                }
               
    else
                {
                   
    return Request.CreateResponse(HttpStatusCode
    .BadRequest);
                }
            }

           
    // POST api/AcadCommands
            public HttpResponseMessage PostCommandsData(UserCommandsHit
    commandsdata)
            {
               
    if
    (ModelState.IsValid)
                {
                    db.UserCommandsHits.Add(commandsdata);
                    db.SaveChanges();

                   
    HttpResponseMessage
    response = Request
                        .CreateResponse(
    HttpStatusCode
    .Created, commandsdata);
                    response.Headers.Location =
    new Uri
    (
                        Url.Link(
    "DefaultApi"
    ,
                               
    new
    { id = commandsdata.Id })
                        );
                   
    return
    response;
                }
               
    else
                {
                   
    return Request.CreateResponse(HttpStatusCode
    .BadRequest);
                }
            }

           
    // DELETE api/AcadCommands/5
            public HttpResponseMessage DeleteCommandsData(int
    id)
            {
               
    UserCommandsHit
    commandsdata = db.UserCommandsHits.Find(id);
               
    if (commandsdata == null
    )
                {
                   
    return Request.CreateResponse(HttpStatusCode
    .NotFound);
                }

                db.UserCommandsHits.Remove(commandsdata);

               
    try
                {
                    db.SaveChanges();
                }
               
    catch (DbUpdateConcurrencyException
    )
                {
                   
    return Request.CreateResponse(HttpStatusCode
    .NotFound);
                }

               
    return Request.CreateResponse(HttpStatusCode
    .OK, commandsdata);
            }

           
    protected override void Dispose(bool
    disposing)
            {
                db.Dispose();
               
    base.Dispose(disposing);
            }
        }
    }

    上面Visual Studio已经自动帮我们创建了数据上下文类,默认是使用SQL Express,你可以看的Web.config里面的连接字符串,稍后我们可以升级到SQL Azure云端数据库。

    、Please note that the context class is using SQL Express, it is easy to migrate to SQL Azure latter:

      <connectionStrings>
       
    <add name="AcadCommandViewerContext" connectionString="Data Source=.\SQLEXPRESS;
    Initial Catalog=AcadCommandViewerContext; Integrated Security=True;
    MultipleActiveResultSets=True"

          providerName="System.Data.SqlClient" />

       

      </
    connectionStrings
    >

    然后为了测试,需要弄点测试数据,在Models目录下添加一个SampleData.cs类来代码生成一点测试数据,如果要做单元测试的话也比较方便。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using
    System.Web;

    namespace
    AcadCommandViewer.Models
    {
       
    public static class SampleData
        {
           
    public static UserCommandsHit danielCmdHits = new UserCommandsHit
            {
                Id = 1,
                UserName =
    "Daniel"
    ,
                CommandHits =
    new CommandHit
    [] {
                       
    new CommandHit
    {
                            CommandName =
    "PLINE"
    ,
                            HitNumber  = 1
                        },
                       
    new CommandHit
    {
                            CommandName =
    "ZOOM"
    ,
                            HitNumber  = 2
                        },
                       
    new CommandHit
    {
                            CommandName =
    "LINE"
    ,
                            HitNumber  = 3
                        },
                       
    new CommandHit
    {
                            CommandName =
    "Save"
    ,
                            HitNumber  = 4
                        }
                    }
            };

           
    public static UserCommandsHit jerryCmdHits = new UserCommandsHit
                 {
                     Id = 2,
                     UserName =
    "Jerry"
    ,
                     CommandHits =
    new CommandHit
    []
                    {
                       
    new CommandHit
    {
                            CommandName =
    "CIRCLE"
    ,
                            HitNumber  = 2
                        },
                       
    new CommandHit
    {
                            CommandName =
    "Quit"
    ,
                            HitNumber  = 1
                        }
                    }
                 };




           
    public static UserCommandsHit[] userCmdsHits = new UserCommandsHit[]
            {
                danielCmdHits,
                jerryCmdHits
            };



        }
    }

    然后需要把这些测试数据生成到数据库中,Visual Studio中的Package Manager Console 很方便。从Tools—> Library Package Manager,打开Package Manager Console ,输入命令:

    PM> Enable-Migrations

    Visual Studio项目中会添加一个Migrations的目录和一个Configuration.cs的类,可以在这个类的Seed方法中来生成初始数据,代码如下:

    namespace AcadCommandViewer.Migrations
    {
       
    using
    System;
       
    using
    System.Data.Entity;
       
    using
    System.Data.Entity.Migrations;
       
    using
    System.Linq;
       
    using
    AcadCommandViewer.Models;

       
    internal sealed class Configuration : DbMigrationsConfiguration<AcadCommandViewer.Models.AcadCommandViewerContext
    >
        {
           
    public
    Configuration()
            {
                AutomaticMigrationsEnabled =
    true
    ;
            }

           
    protected override void Seed(AcadCommandViewer.Models.AcadCommandViewerContext
    context)
            {
               
    //  This method will be called after migrating to the latest version.

               
    //  You can use the DbSet<T>.AddOrUpdate() helper extension method
                //  to avoid creating duplicate seed data. E.g.
                //
                //    context.People.AddOrUpdate(
                //      p => p.FullName,
                //      new Person { FullName = "Andrew Peters" },
                //      new Person { FullName = "Brice Lambson" },
                //      new Person { FullName = "Rowan Miller" }
                //    );
                //

               
    //SampleData s = new SampleData();
                context.UserCommandsHits.AddOrUpdate(
                    p => p.UserName,
                   
    SampleData.userCmdsHits
                    );
            }
        }
    }

    然后在package Manager Console 中运行Update-Database命令在数据库中生成数据:

    PM> Update-Database

    好了,到目前为止,服务端程序就创建完毕了,这个程序已经能够以REST的方式提供服务了,包括数据的获取,上传,更新和删除,对于REST服务的测试,可以使用Fiddler工具,这个工具的使用,你可以自行搜索,也可以看我们的另外一篇英文博客。 this post.

    下来回继续接受浏览器端WebGL部分的实现。敬请关注。

  • 相关阅读:
    LeetCode 452. 用最少数量的箭引爆气球
    LeetCode 451. 根据字符出现频率排序
    LeetCode 464. 我能赢吗
    LeetCode 2. 两数相加
    @RendSection{"scripts",require:false}的作用
    js中变量含(参数、数组)作用域传递问题
    Web Api通过Route、RoutePrefix等特性设置路由
    SQLserver中存储图片
    RDLC的部署(无法找到Microsoft.ReportViewer.ProcessingObjectModel.dll文件)
    C# 发送Http协议 模拟 Post Get请求
  • 原文地址:https://www.cnblogs.com/junqilian/p/2955316.html
Copyright © 2020-2023  润新知