Friday, September 20, 2013

Steps to setup Apache reverse proxy on Window 7

1. Download Apache web server for Windows binary and install it.
2. In httpd.conf, update the listening port number
Listen 7000
3. uncomment the following lines to load the required modules
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule ssl_module modules/mod_ssl.so
4. enable virtual host by uncomment the following line
Include conf/extra/httpd-vhosts.conf
5. Edit : conf\httpd-vhosts.conf, for example, the following settings can be used to redirect request
from
torn004613401a.dhcp.tor2.sap.corp:7000/proxy/entity/odata
to
torn00461340a.dhcp.tor2.sap.corp/entity/odata, 


NameVirtualHost *:7000
<VirtualHost *:7000>
    ServerName torn00461340a.dhcp.tor2.sap.corp
    ServerAlias dymmyhost
    ErrorLog "logs/dummy-host.dhcp.tor2.sap.corp-error.log"
    CustomLog "logs/dummy-host.dhcp.tor2.sap.corp-access.log" common
    ProxyPass /proxy/ http://torn00461340a.dhcp.tor2.sap.corp/
    ProxyPassReverse /proxy/ http://torn00461340a.dhcp.tor2.sap.corp/

</VirtualHost>

Note extra steps are required to handle cookie path and domain and https connection.

Wednesday, September 4, 2013

Steps to create a CRUD oData service with ASP.Net Web API

Quite a lot of ASP.Net Web API shows how to create a read-only oData service, which is not very useful in real project, the following steps can be used to create a full CRUD webapi odata service that can run on iis. The project is inspired by pluralsight's ASP.Net oData training project as well as other online help information.

Tools:
VS 2012 on Windows 7

1. Create a new MVC 4 project
2. Add Entity Framework and import Northwind database, use Windows integrated mode for database connection
3. Add WebApi oData package using NeGet, the ApiController does not work with circular json serialize, so it has to use EntitySetController to create controller class.
4. update WebApiConfig.cs as follows
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapODataRoute("Northwind", "odata", GetImplicitEDM());

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
           ....
        }

        private static IEdmModel GetImplicitEDM()
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            var customers = builder.EntitySet<Customer>("Customers");
            builder.EntitySet<Order>("Orders");
            builder.EntitySet<Shipper>("Shippers");
            return builder.GetEdmModel();
        }
    }

5. create a new CustomersController class, the name needs matching the EntitySet name.
public class CustomersController : EntitySetController<Customer, string>
{
   NORTHWNDEntities _context = new NORTHWNDEntities();

        [Queryable]
        public override IQueryable<Customer> Get()
        {
            return _context.Customers;
        }

        protected override Customer GetEntityByKey(string key)
        {
            return _context.Customers.FirstOrDefault(c => c.CustomerID == key);
        }

        [Queryable]
        public IQueryable<Order> GetOrdersFromCustomer([FromODataUri] string key)
        {
            return _context.Orders.Where(o => o.CustomerID == key);
        }

        protected override string GetKey(Customer entity)
        {
            return entity.CustomerID;
        }

        //sample request form fiddler
        //header
        //Content-Type: application/json
        //payload
        //{"CustomerID":"ABCDE","CompanyName":"ABCDE","ContactName":"ABCDE","ContactTitle":"ABCDE"}
        protected override Customer CreateEntity(Customer entity)
        {
            _context.Customers.Add(entity);
            _context.SaveChanges();
            return entity;
        }

        //sample request from fiddler, only specified fields from client will be set to the new objects, all other
        //fields will be set to null
        //url
        // put for http://localhost:58317/odata/Customers('ABCDE')
        //header
        //Content-Type: application/json
        //payload, customerid field will be ignored
        //{"CustomerID":"AAAA","CompanyName":"AAAA"}
        protected override Customer UpdateEntity(string key, Customer update)
        {
            if (!_context.Customers.Any(c => c.CustomerID == key))
            {
                throw new HttpResponseException(
                    Request.CreateODataErrorResponse(
                      HttpStatusCode.NotFound,
                      new ODataError
                      {
                         ErrorCode = "EntityNotFound",
                         Message = "Customer key " + key + " not found"
                      }));
            }

            update.CustomerID = key;
            _context.Customers.Attach(update);
            _context.Entry(update).State = System.Data.EntityState.Modified;
            _context.SaveChanges();
            return update;
        }

        //Similar to post but keeping the existing fields in object if client side does not specify them
        protected override Customer PatchEntity(string key, Delta<Customer> patch)
        {
         
            var customer = _context.Customers.FirstOrDefault(c => c.CustomerID == key);

            if (customer == null)
            {
                throw new HttpResponseException(
                    Request.CreateODataErrorResponse(
                      HttpStatusCode.NotFound,
                      new ODataError
                      {
                          ErrorCode = "EntityNotFound",
                          Message = "Customer key " + key + " not found"
                      }));
            }

            patch.Patch(customer);
            _context.SaveChanges();
            return customer;
        }

        //sample request from fiddler, only specified fields from client will be set to the new objects, all other
        //fields will be set to null
        //url
        //delete for http://localhost:58317/odata/Customers('ABCDE')
        //header
        //Content-Type: application/json
        public override void Delete([FromODataUri] string key)
        {

            var customer = _context.Customers.FirstOrDefault(c => c.CustomerID == key);

            if (customer == null)
            {
                throw new HttpResponseException(
                    Request.CreateODataErrorResponse(
                      HttpStatusCode.NotFound,
                      new ODataError
                      {
                          ErrorCode = "EntityNotFound",
                          Message = "Customer key " + key + " not found"
                      }));
            }
            _context.Customers.Remove(customer);
            _context.SaveChanges();
        }


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

6. Start the app on IIS express, it should handle the oData request. But it will fail when running on IIS, continue the following steps to run it on IIS
7.Restart VS 2012 in admin mode, and change the project setting to create IIS virtual directory and use IIS for testing
8. Edit C:\Windows\System32\inetsrv\config\applicationHost.config for the following part
<add name="ASP.NET v4.0" autoStart="true" managedRuntimeVersion="v4.0" managedPipelineMode="Integrated">
<processModel identityType="ApplicationPoolIdentity" loadUserProfile="true" setProfileEnvironment="true" /></add>

(refer to http://blogs.msdn.com/b/sqlexpress/archive/2011/12/09/using-localdb-with-full-iis-part-1-user-profile.aspx and http://blogs.msdn.com/b/sqlexpress/archive/2011/12/09/using-localdb-with-full-iis-part-2-instance-ownership.aspx for details if you still get database connection error).

Tuesday, September 3, 2013

Some note about Linq, WCF, EntityFramework, WebApi, oData

The key to understand the syntax of linq, is it is built based on IEnumerable, so everything is based on a single element from the IEnumerable collection, if you see a variable declaration coming out of nowhere, then you can think of it as a instance of a IEnumerable instance. This is particular useful to understand Lambda syntax

WCF provides an easy way to implemenat oData service, but it is used mainly for .net client. If you create a oData service to support ios, Android devices, then it is better to create the oData service with WebApi, developer has better control over web API then WCF's customization.

EntityFramework helps to expose the backend data as objects, but when using WebApi, it is not very stable to serialize the dependent objects included in the foreign key field, the easy solution is just returning the foreign key field as a simple field data instead of returning it as a child  object. As for mobile client, it is not proper to return all objects included in object mapping, if client needs dependent objects, it is better to let client send another separate request.

When using EntitySetController to build oData endpoint, be sure the controller name matches the entity name, otherwise the resource cannot be found:

  private static IEdmModel GetImplicitEDM()
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Customer>("Customers");
            builder.EntitySet<Order>("Orders");
            builder.EntitySet<Order_Detail>("OrderDetails");
            return builder.GetEdmModel();
        }

public class CustomersController : EntitySetController<Customer, string>
   

Fiddler: Set username and password for basic authentication

1. open fiddler, click Tools->TextWizard... menu item,
2. select checkbox of "To Base64"
3. input your user name and password in top textbox such as
myUsername:myPassword
4. Go back to fiddler composer screen and add a header of below, the last past is the output of
Authorization: Basic bXlVc2VybmFtZTpteVBhc3N3b3Jk

Sunday, July 7, 2013

How to get apple developer provision profile id

When each time the provision profile is updated in apple developer portal website, downloading the profile will get a new id. If the id does not match, the build will fail because of the signing error saying "the provision profile XXXXXX cannot be found".
However, it seems xcode organizer does not show the profile id for available profiles for identitying the signing error. Neither the Apple developer portal will show this profile id when downloading the profile.
Fortunately, the provision profile id is shown on the iphone configuration tool. So it can be used to find out which profile has which id required by signing.

xcode build settings for debug symbol

Generate debug symbol 
The debug symbol for xcode project is controlled by project settings of "Apple LLVD compiler 4.2"/"generate debug symbol". The recommendation is setting it to YES for both debug and release build. So that it can be used for crash report symbolization.
For application, this will generate a separate dsym file in build output folder. For static library, this will only increase the size of the generated a file

Deployment postprocessing
This is a generic control to decide whether other deployment operation should be performed, particular, Strip Linked Product is executed or not. 
For static library, the recommended setting is NO for both release and debug build, to not remove symbols.
For application build, the recommended setting is Yes for release build and No for debug build. So that release build can have a smaller size.

Strip Debug Symbols During copy
This flag decides when copying the dependent library for building the project, should the library's symbol be removed. It does not affect the build output binary. 
The recommended setting is set to NO for both release and debug build, as the build output needs to include the dependent libraries' symbol to debug or symbolize the library code. 
(It seems for ios project, this setting does not have any effect.)

Strip linked product
This flag will reduce the size of the executable by removing the symbol from it. But it will cause the crash dump not have any symbol, and it will need a separate symbol file to symbolize the crash report.
The flag will not change sym file size.
For library, the recommended setting is NO, so it includes the debug symbol in the .a file
For application, if the app size matters, set it to YES. otherwise, set it to NO.








Friday, July 5, 2013

"Build active architecture only" setting for ios xcode projecgt

When building ios static library, "build active architecture only" does not affect simulator build, it only builds i386 architecture without any armv7 or armv7s build. However, for device build, if it is set to NO. Then all available architecture will be built (armv7 and armv7s); if it is set to YES, then only the active architecture will be built. But how it decide what is the current active architecture? actually, xcode decides it based on build destination, that is, the connected device or simulator type selectedfrom the build type dropdown listbox.
If the current device is armv7s, then it will build armv7s output. If no real device is connected, then it will build armv7 output, which will also support older device types.