Category: .Net Development

Neo4jClient Primer

Introduction

Neo4jClient is a .NET client for the Neo4j Rest Server built by a colleague of mine Tatham Oddie. Currently we have a Neo4jClient, which is the latest source code build. This can be found on NuGet with package name Neo4jClient- Neo4jClient is a .NET client for the Neo4j Rest Server.

NuGetPackage:
Source Code at:

It leverages RestSharp and Newtonsoft.json for Rest and Json serialization/deserialization respectively.

The client also supports executing Gremlin queries in the following ways

  • Send raw gremlin statements that return scalar results
  • Send raw gremlin statements that return enumerable collection of nodes
  • Type safe lambda expressions

The raw version is provided to provide flexibility if you find certain lambda expressions are not supported, over time, more expressions will be added. We will be extending the client over time to certain more expressions as we find ourselves needing new expressions.

Lets have a look at some samples.

First you will need to install the package, so at the package console manager type:

install-package neo4jclient

Once this is installed, you can then of course load the IGraphClient in the entry point of your application, manually or using an IoC. The concept of the GraphClient is to provide basic CRUD operations.

The other powerful aspect is that the IGremlinQuery interface provide cool extensions into Gremlin so that a series of extensions methods will support out and in vertices and edges querying.

Below is a diagram illustrating the core concept.Basically we have a GraphClient, GremlinQuery and NodeReference. Notice that you can also query directly off a NodeReference. A NodeReference will represent a reference to a Vertice in the database. A Node will store the actual data, which is cast to a specific type with generics.

image

Entry Point

Here is sample code in loading the GraphClient using an IoC. It is this easy to get it started.

 builder.Register<IGraphClient>(context =>
            {
                var resolver = context.Resolve<IRoleEndpointResolver>();
                var baseUri = resolver.GetNeo4JBaseUri();
                var graphClient = new GraphClient(baseUri);
                graphClient.Connect();
                return graphClient;
            })
            .SingleInstance();

Type Safe Lambda Expressions

Lets look at the cool features we can do. Below is a sample query we can run.

public Node<User> GetUser(User identifier)
        {
            if (identifier.IsSysAdmin)
            {
                return graphClient
                    .RootNode
                    .In<User>(Administers.TypeKey, u=> u.Username == identifier.Username)
                    .SingleOrDefault();
            }

            return graphClient
                .RootNode
                .Out<CustomerSite>(Hosts.TypeKey, a => a.Key == identifier.CustomerSiteKey)
                .In<User>(UserBelongsTo.TypeKey, u => u.Username == identifier.Username)
                .SingleOrDefault();
        }

You can even then run queries off a NodeReference, lets look at an example.

public int CreateUser(User user, NodeReference<Company> companyReference)
{
    return companyReference
                .InE(UserBelongsTo.TypeKey)
                .OutE<User>(u=>u.Username == user.Username)
}

You have the flexibility.

Creating Nodes and Relationships

You notice that in the above, we had a TypeKey representing the relationship, this is important, you can enforce very strict rules on your nodes, we can define a class that represents a relationship and enforce which source and target nodes or data models it is allowed to have e.g.

    public class UserBelongsTo :
        Relationship,
        IRelationshipAllowingSourceNode<User>,
        IRelationshipAllowingTargetNode<CustomerSite>
    {
        public UserBelongsTo(NodeReference targetNode)
            : base(targetNode)
        {
        }

        public const string TypeKey = "USER_BELONGS_TO";
        public override string RelationshipTypeKey
        {
            get { return TypeKey; }
        }
    }

This means, that you get compile time safety, if you try and create a node in the database with a relationship. Lets look at a Create statement.

        public NodeReference CreateUser(User user, NodeReference<CustomerSite> customerSite)
        {
            user.Username = user.Username.ToLowerInvariant();

            var node = graphClient.Create(
                user,
                new UserBelongsTo(customerSite));

            return node;
        }

Above, if you tried to swap the create, it would not work e.g.

        public NodeReference CreateUser(User user, NodeReference<CustomerSite> customerSite)
        {
            user.Username = user.Username.ToLowerInvariant();
            var node = graphClient.Create(
                customerSite,
                new UserBelongsTo(user));

            return node;
        }

Updates

You can also update nodes, this is done by passing in a NodeReference and a delegate e.g.

public void UpdateUser(User user, Action<User> updateCallback)
{
            graphClient.Update(userNode.Reference, u =>
                {
                    updateCallback(u);
                });
}

Notice, you also get type safety here as well. The reference to the delegate/method with then get executed when neo4jClient persists the data.

Here is the sample updateCallback call to the above method. Notice I am in fact using the MVC updateModel facility to do the mappings for me, any how, you can update the object using your logic of course, there is no restrictions here. Here “this” refers to the MVC Controller class. It is just a nice way to get MVC to compare the user with the user from the DB and then merge the changes, no need to write logic to merge changes if using in the context of MVC, since it has a nice UpdateModel method that we can use. Othewise you would be using AutoMapper or something even nicer like ValueInjecter(http://valueinjecter.codeplex.com/).

userService.UpdateUser(user, u => this.UpdateModel(u,
                UpdateModel,
                tu => tu.GivenName,
                tu => tu.FamilyName,
                tu => tu.Username,
                tu => tu.EmailAddress,
                tu => tu.BusinessPhone,
                tu => tu.MobilePhone
            ));

The update above looks a bit tricky at first as we see a method pointer to a method pointer i.e. two action calls, in fact there are 4, one from the graph client, one from the service, one from the extended version of UpdateModel and then the updateModel itself.

The reason why I have a custom extension method for UpdateModel, is so we can Explicitly set which columns to update. Remember UpdateModel takes a callback and a list of properties to explicitly update, you can of course just call the default version if ALL fields need to be updated.

userService.UpdateUser(user, UpdateModel);

Below is the code for the extended UpdateModel.

 public static class ControllerExtensions
    {
        public static void UpdateModel<TModel>(this Controller controller, TModel instanceToUpdate, Action<TModel, string[]> updateModelCallback, params Expression<Func<TModel, object>>[] includeProperties)
        {
            var propertyNames = GetPropertyNames(includeProperties);
            updateModelCallback(instanceToUpdate, propertyNames.ToArray());
        }

        public static IEnumerable<string> GetPropertyNames<T>(params Expression<Func<T, object>>[] action)
        {
            return action
                .Select(property =>
                {
                    var body = property.Body;

                    var unaryExpression = body as UnaryExpression;
                    if (unaryExpression != null) body = unaryExpression.Operand;

                    return (MemberExpression) body;
                })
                .Select(expression => expression.Member.Name);
        }
    }

The above extension method will now allow you to call the UpdateModel with type safety on the model fields to explicitly update. As i mentioned, if you need a simpler update to default to all fields then this call will work:

Deletes

You can also delete data. Notice the pattern here, get a node reference then run an operation.

graphClient.Delete(node.Reference,DeleteMode.NodeAndRelationships);

Relationships

You can also create relationships between existing nodes.

graphClient.CreateRelationship(customerNodeReference, new Speaks(languageNode.Reference));

Scalar Query Result – Raw

You might find that a complex lambda is not support, in which case you can execute a raw statement directly an still get type safety e.g.

var count = g.v(0).out('IsCustomer').Count()

IEnumerable Query Result – Raw

We can also do raw queries that return an enumerable of a node.

var users = graphClient.ExecuteGetAllNodesGremlin<IsCustomer>("g.v(0).out('IsCustomer'){it.'Name' == 'BobTheBuilder'}");

Rest under the hood

The Client is smart enough to query the rest endpoint on the server and utilize the correct rest points. So the baseUri would be something like http://locahost:7474/db/data

The graphclient will do a Http get request with application/json to the above endpoint and will retrieve the following response.

{
  "index" : "http://localhost:7474/db/data/index",
  "node" : "http://localhost:7474/db/data/node",
  "reference_node" : "http://localhost:7474/db/data/node/0"
}

The above endpoints is the fundamental way to run rest queries, of course the graphclient does all the work for you, however it is always nice to know how it works.

Summary

So this is my little primer on the project we working with and it is fantastic working with someone like Tatham Oddie who built this brilliant tool for us to use.

We will be extending this tool as we go along and build new functionality as we need it. Not all Lambda expressions are supported yet and it is limited in tis regard but it is easy to extend going forward.

Neo4j and gremlin plugin install guide

29/08/2011: OBSOLETE – Now baked into the Core of Neo4j.

Hi,

I was having some difficulties getting the Gremlin query plugin working correctly with the Neo4j server which we will host on a Windows Azure VM.

Below is some steps to get this working nicely.

Firstly you will of course need to have Neo4j running. Then all we need to do is install the following:

Java JDK – Here is my version
java version “1.6.0_26”
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)

JDK is needed to compile the plugin.

Maven 2.2.1 (There is compilation errors with SnapShot compiles with 3.0.3 at time of writing)
http://www.apache.org/dyn/closer.cgi/maven/binaries/apache-maven-2.2.1-bin.zip

Also, we need MVN, this is used to compile the Gremln Plugin. I was have problems with

Neo4j gremlin plugin
https://github.com/neo4j/gremlin-plugin

I like to setup environment variables to Neo4j server folder, java_home and also the maven location.

image

Once done we can then compile the plugin and copy it into the Neo4j plugins folder.

mvn clean package 
copy target\neo4j-gremlin-plugin-0.1-SNAPSHOT.jar $NEO4J_HOME\plugins 
cd $NEO4J_HOME\bin\neo4j.bat restart

Compiled version of the plugin.

image

Here we can see the plugin in the folder.

image

Now, to ensure Neo4j has the plugin, we can execute a curl command to check the extension is installed:

C:\Users\Romiko>curl localhost:7474/db/data/
{
  "relationship_index" : "http://localhost:7474/db/data/index/relationship",
  "node" : "http://localhost:7474/db/data/node",
  "relationship_types" : "http://localhost:7474/db/data/relationship/types",
  "extensions_info" : "http://localhost:7474/db/data/ext",
  "node_index" : "http://localhost:7474/db/data/index/node",
  "reference_node" : "http://localhost:7474/db/data/node/0",
  "extensions" : {
    "GremlinPlugin" : {
      "execute_script" : "http://localhost:7474/db/data/ext/GremlinPlugin/graphd
b/execute_script"
    }
  }
}

As we can see above, the rest result from the server has the GremlinPlugin Extension. In fact we can now do an HTTP Post Gremlin query to get the nodes from the object graph in the database.

e.g.

I want to see if I have a Node at the second level that has a relationship of type Related To with the Out Direction.

g.v(1).outE(‘RELATED_TO’)

Now we need to URL encode this.

+g.v(1).outE(%27RELATED_TO%27)

curl -d “script=+g.v(1).outE(%27RELATED_TO%27)” http://localhost:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

as we can see the output is:

C:\Users\Romiko>curl -d “script=+g.v(1).outE(%27RELATED_TO%27)” http://localhost

:7474/db/data/ext/GremlinPlugin/graphdb/execute_script

[ {

“start” : “http://localhost:7474/db/data/node/1″,

“data” : {

},

“self” : “http://localhost:7474/db/data/relationship/23″,

“property” : “http://localhost:7474/db/data/relationship/23/properties/{key}”,

“properties” : “http://localhost:7474/db/data/relationship/23/properties”,

“type” : “RELATED_TO”,

“extensions” : {

},

“end” : “http://localhost:7474/db/data/node/2″

} ]

We have Node 23 being related to Node 2. (Remember Node 0, then Node 1).

the above result can be confirmed in the gremlin console (now baked into Neo4j) as of June 2011.

Note: The gremlin extension that is now part of theneo4j server\lib extensions is not for rest API queries, you still need this plugin!

  • gremlin> g.v(1).outE(‘RELATED_TO’)
  • ==> e[23][1-RELATED_TO->2]
  • gremlin>

Here is a screenshot of the gremlin console now baked into Neo4j.

image

image

Hope this gets you started with Neo4j and the gremlin plugin query language Smile

I might be looking at building a custom IQuerable expression translation, so we can then use Linq to query a gremlin based API. Might be fun to do, but first need to learn more about gremlin and Neo4j.

There is a fluent API for gremlin queries you can leverage as a .Net client:

NuGetPackage:
Source Code at:

Cheers

WCF Architecture/Extensibility Overview

Hi Guys,

This post is going to discuss some basic high level aspects of WCF. Below is a diagram of the architecture for it.

Some high level facts.

  • Message Contract is the structure of an actual WCF Message, it describes the nature of the message
  • A Data Contract is the PAYLOAD or actual data, which is embedded into the Message.
  • The Service runtime is primarily concerned with processing the content of message bodies
    • The message layer is concerned with “channels” and channel stacks (More than one channel)
      There are two types of channels
      Protocol Channel – Message Header Management – WS-Security/WS-Reliability
      Transport Channel – How data is communicated/translated/encoded/decoded on the wire. Http, Netmsmq
    • Hosting – WCF can be hosted in a Windows Service, Executable, IIS WAS or IIS. You can even run it inside a NServiceBus host if you wanted.

We all know the ABC’s of WCF.

A service will need an Address, Binding and a Contract. But there is allot more to WCF than meat the eye.

Behaviors
Control various run-time aspects of a service, an endpoint, a particular operation, or a client. You have common behaviors affect all endpoints globally,
Service behaviors affect only service-related aspects,
Endpoint behaviors affect only endpoint-related properties, and
Operation-level behaviors affect particular operations.

e.g. One service behavior is throttling, which specifies how a service reacts when an excess of messages threaten to overwhelm the system. An endpoint behavior, such as where to find a security credential.

In regards to service behaviours, one aspect that is overlook is Instance and Concurrency modes. Read more about it further down in this article.

Instances and Concurrency

This is often overlook, always be aware of how you write your WCF service and ensure the code is thread safe and can handle multiple instances and concurrency aspects, else you might find your WCF services not scalable! These are things you should always think about BEFORE your write the service. You should read this article to get a better understanding of it.

http://msdn.microsoft.com/en-us/library/ms731193.aspx

Have a read, and ensure you classes etc are thread safe so they can scale, no shared variables etc in your WCF code that maintain a state at the service layer, you will find yourself in deep water. You can use sessions, instances or concurrency mode combinations to control these aspects.

Here is an interesting  example of customizing this option, which should get you thinking about how you combine these sort of behavioural modes!

 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
    public class ProductsService : IProductsService
    {
      //Your Service Logic
     }

Per-call services are the Windows Communication Foundation default instantiation mode. When the service type is configured for per-call activation, a service instance, a common language runtime (CLR) object, exists only while a client call is in progress. Every client request gets a new dedicated service instance. This keeps the lifetime of objects short as possible.

  1. The client calls the proxy and the proxy forwards the call to the service.
  2. Windows Communication Foundation creates a service instance and calls the method on it.
  3. After the method call returns, if the object implements IDisposable, then Windows Communication Foundation calls IDisposable.Dispose on it.

Single: Each instance context is allowed to have a maximum of one thread processing messages in the instance context at a time. Other threads wishing to use the same instance context must block until the original thread exits the instance context.

Can you see something here, this combination is irrelevant? Since PerCall is here,the proxy will never allow multiple threads, since the proxy will know there is an instance already, so some combinations will never need to be explicit, such as the redundant attributes in the above code.

Dispatcher Runtime And Client Runtime

What if you want to customize Wcf? How about introducing a custom Encoding/Decoding Algorithm or custom compression/validation system or a custom error handler for legacy systems?

Lets get even more fancy, how about a custom instance provider that can hydrate and dehydrate WCF instances to and from a database for  long running transactions, similar to the idea of Saga’s in NServiceBus…

This can all be done on the client runtime or dispatcher on the service.

There is allot going on in WCF and there is several posts on making a Hello World WCF service, lets skip all that and get down to extensibility of WCF. We will focus on the dispatcher and message inspectors. Lets check what we can do firstly on the client side and then on the server side.

Here is an overview of the architecture.

image

From the above diagram you can see that WCF is very extensible, there are hooks in the architecture where you can extend the functionality of WCF.

The  client runtime is responsible for translating method invocations into outbound messages, pushing them to the underlying channels, and translating results back into return values and out parameters.

This runtime model presents different service model extensions to modify or implement execution or communication behavior and features client or dispatcher functionality such as message and parameter interception, operation selection, message encoding and other extensibility functionality.

In the service, the dispatcher runtime is responsible for pulling incoming messages out of the underlying channels, translating them into method invocations in application code, and sending the results back to the caller. This runtime model presents different service model extensions to modify or implement execution or communication behavior and features client or dispatcher functionality such as message and parameter interception, message filtering, encoding and other extensibility functionality.

There is numerous examples here:

http://msdn.microsoft.com/en-us/library/ff183867.aspx

Here is an example of a WCF Service using a Behaviour for JSON Serialization. Notice the different level of behaviours from EndPoint Behaviours/Service Behavoirs etc, also notice we have a JSON which is done in the EndPoint Behaviour. Also notice the bindings, we have different types for different clients, .Net can use the webHttpBinding and Java clients can use the BasicHttpBinding.

<system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="BaseBehaviors">
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True" httpGetUrl="Products/GetList" httpsGetUrl="Products/GetList" />
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="BaseHttpEndpointBehavior">
        </behavior>
        <behavior name="jsonBehavior">
          <enableWebScript  />
        </behavior>
      </endpointBehaviors>
    </behaviors>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" />

    <services>
      <service behaviorConfiguration="BaseBehaviors" name="Romiko.MyService">
        <endpoint name="ProductService" address="Products" behaviorConfiguration="BaseHttpEndpointBehavior"
          binding="basicHttpBinding" bindingConfiguration=""
          contract="Romiko.IProductsService" />
        <endpoint name="ProductServiceSSL" address="ProductsSSL" behaviorConfiguration="BaseHttpEndpointBehavior"
          binding="basicHttpBinding" bindingConfiguration="SecureSSL"
          contract="Romiko.IProductsService">
        </endpoint>
        <endpoint name="ProductsServiceJSON" address="ProductsJSON" behaviorConfiguration="jsonBehavior"
                  binding="webHttpBinding" bindingConfiguration=""
                  contract="Romiko.IProductsProductsService" />
        <endpoint name="ProductsServiceJSONSSL" address="ProductsJSONSSL" behaviorConfiguration="jsonBehavior"
                  binding="webHttpBinding" bindingConfiguration="SecureSSLWeb"
                  contract="Romiko.IProductsService">
        </endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost/Products" />
            <add baseAddress="https://localhost:443/Products" />
          </baseAddresses>
        </host>
      </service>
    </services>
    
    
    <bindings>
      <basicHttpBinding>
        <binding name="SecureSSL">
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </basicHttpBinding>
      <webHttpBinding>
        <binding name="SecureSSLWeb">
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </webHttpBinding>
    </bindings>
  </system.serviceModel>

 

Well, I hope this helps you get your toes a little deeper into WCF, so the next time you write a WCF service you can nut out all the architectural principles BEFORE writing the code.

References:

http://msdn.microsoft.com/en-us/library/ff183867.aspx

http://msdn.microsoft.com/en-us/library/ms733128.aspx

PayPal Payment Standard IPN/PDT–Asynchronous Processing

Hi Guys,

I am currently working on a personnel project to interface with PayPal Payment Standard on an MVC3 and Windows Azure based application. I needed to find a nice way to convert request/response objects from PayPal to their corresponding IPN/PDT objects.

I want a payment solution that can be easily scaled on high load. So we will leverage NServiceBus as the front end –> back end service bus infrastructure. This allows decoupling of payments from the functionality of the site, so under high load the payments will not restrict the usability of the site, This is done with MSMQ and NServicebus. I will show a basic example of how.

Below is a DTO you can use for IPN or PDT data. Of course you will need logic to convert http request/response data to this DTO and you have many ways of doing so.

More information about PDT/IPN variables can be found here:

https://www.paypal.com/IntegrationCenter/ic_ipn-pdt-variable-reference.html
 https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables#id091EB0901HT

You can then take and IPN and convert the request form/query string values  to a DTO e.g.

  _myIpnNotification.Invoice = _myRequest["invoice"];

The same applies for the PDT which you will receive out of band on a separate listener.

 

Here are the different type of transaction types and status

 [Serializable]
    public enum PdtStatus
    {
        Unknown = 0,

        [Description("SUCCESS")] Success,
        [Description("FAIL")] Fail
    }

  [Serializable]
    public enum IpnStatus
    {
        Unknown,
        [Description("verified")] Verified,
        [Description("invalid")] Invalid
    }

    [Serializable]
    public enum TransactionType
    {
        //[Description(null)]
        Unknown = 0,

        [Description("cart")] Cart,

        [Description("express_checkout")] ExpressCheckout,

        [Description("merch_pmt")] MerchantPayment,

        [Description("send_money")] SendMoney,

        [Description("virtual_terminal")] VirtualTerminal,

        [Description("web_accept")] WebAccept,

        [Description("masspay")] MassPayment,

        [Description("subscr_signup")] SubscriptionSignUp,

        [Description("subscr_cancel")] SubscriptionCancellation,

        [Description("subscr_failed")] SubscriptionPaymentFailed,

        [Description("subscr_payment")] SubscriptionPayment,

        [Description("subscr_eot")] SubscriptionEndOfTime,

        [Description("subscr_modify")] SubscriptionModification
    }

Security, if you want the best security, never trust anything from Paypal, always double check the data, so when an IPN comes in, take that data and do a check with Payal and then verify the results match, and the same with PDT data.

This means checking the IPN data – currency, amount, date, subscription type etc is the same as what is in your transaction log, this way, any spoofing is protected, here is an example of a rule.

 public interface IRulesCommon
    {
        List<Error> ApplyRulesStandardPayments( decimal resultAmount, Currency resultCurrency, string receiver, TransactionLog transaction);
    }
 public interface IRulesPaypal
    {
        List<Error> ComparePaymentFrequencyTransactionStatus(TransactionLog transaction, TransactionType transactionType);

        //Used by IPN Only for SignUp
        List<Error> CheckPaymentFrequencyOnSignupIpn(TransactionLog transaction, TimePeriod subscriptionPeriod);

        List<Error> CheckSubscriptionPayment(decimal mcAmount3, Currency currency, string receiver,
                                             TransactionLog transaction);



        List<Error> CheckSubscriptionSignUpCancel(decimal mcAmount3, string recurring, Currency currency, string receiver,
                                     TransactionLog transaction);
    }

 

Unfortunately, I cannot show you implementation logic for this, as it may breach the security of my site if something is found which can be compromised, however this interface is a good start.

As you can see above, I do allot of checks, I ensure the amount, currency, receiver all match the original transaction object.

So, now we have an IPN or PDT listener, so say I have an MVC3 listener controller like so:

 public class IpnController : Controller
    {
        public readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        readonly IBus _bus; //NServiceBus Object
        readonly IIpnDataProvider _ipnDataProvider;
        readonly ISettingsDataProvider _settings;

        
        //Constructor Inject with Autofac
        public IpnController(IBus bus, IIpnDataProvider ipnDataProvider, ISettingsDataProvider settings)
        {
            _bus = bus;
            _settings = settings;
            _ipnDataProvider = ipnDataProvider;
        }

        /// <summary>
        /// Expects a post with variables like: ?mc_gross=19.95&protection_eligibility=Eligible&address_status=confirmed&payer_id=LPLWNMTBWMFAY&tax=0.00&address_street=1+Main+St&payment_date=20%3A12%3A59+Jan+13%2C+2009+PST&payment_status=Completed&charset=windows-1252&address_zip=95131&first_name=Test&mc_fee=0.88&address_country_code=US&address_name=Test+User&notify_version=2.6&custom=1||1||myredirecturl&payer_status=verified&address_country=United+States&address_city=San+Jose&quantity=1&verify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtf&payer_email=gpmac_1231902590_per%40paypal.com&txn_id=61E67681CH3238416&payment_type=instant&last_name=User&address_state=CA&receiver_email=gpmac_1231902686_biz%40paypal.com&payment_fee=0.88&receiver_id=S8XGHLYDW9T3S&txn_type=express_checkout&item_name=&mc_currency=USD&item_number=&residence_country=US&test_ipn=1&handling_amount=0.00&transaction_subject=&payment_gross=19.95&shipping=0.0
        /// </summary>
        /// <returns></returns>
        public ActionResult Ipn()
        {
            string rawData = Extract.GetHttpRawData(Request);

            if (_settings.AuditHttpEnabled)
                Audit.AuditHttp(rawData, _bus, Request.RawUrl); //Asynchronous auditing of the IPN message for audit tracking

            if (_ipnDataProvider != null && _ipnDataProvider.MandatoryDataSpecified)
            {
                try
                {
                    var message = new PaypalIpnMessage
                        {
                            OriginalHttpRequest =
                                rawData,
                            MessageId = Guid.NewGuid(),
                            InvoiceId = _ipnDataProvider.TransactionId,
                            Notification =
                                new ResponseToIpnNotification(Request.Form).GetIpnNotification()
                        };
                    _bus.Send(message); //Asynchronous processing of the payment message to the backend systems
                }
                catch (Exception e)
                {
                    Log.Info(e);
                    Log.Info(rawData);
                    return new HttpStatusCodeResult(400);
                }
                return View();
            }

            Log.Info("Request data does not contain mandatory fields e.g. MerchantId and MerchantTransactionId");
            Log.Info(rawData);
            return new HttpStatusCodeResult(“_codeResult”);
        }
    }

 

As you can see above, any payment processing is now totally decoupled, the Front End and Back End work independent of each other with no synchronous calls. Yes this is all wrapped within a MSDTC transaction. I prefer this than using WCF, due to no issue in dealing with latency and I have guaranteed delivery of my crucial message.,You will write a similar controller for the PDT out of band response as well. We also leveraging an IoC container to automatically inject the service bus object into the controller as well as other dependencies. My favourite is Autofac.

 public ActionResult Process()
        {
            string rawData = Extract.GetHttpRawData(Request);
            if (_settings.AuditHttpEnabled)
                Audit.AuditHttp(rawData, _bus, Request.RawUrl);

            if (_successDataProvider != null && _successDataProvider.MandatoryDataSpecified)
            {
                try
                {
                    var message = new PaypalPdtMessage
                        {
                            OriginalHttpRequest =rawData,
                            MessageId = Guid.NewGuid(),
                            TransactionIdForPdtToken = _successDataProvider.TransactionIdForPdtToken,
                            AmountPaidRecordedByPdt = _successDataProvider.AmountPaidRecordedByPdt,
                            InvoiceId = _successDataProvider.TransactionId,
                            
                        };

                    _bus.Send(message);
                   
                }
                catch (Exception e)
                {
                    Log.Info(e);
                    Log.Info(rawData);
                }

                return Redirect(_successDataProvider.MerchantRedirectURL);
            }


            Log.Info("Request data does not contain mandatory fields e.g. MerchantId");
            Log.Info(rawData);
            return new HttpStatusCodeResult(“_codeResult”);
        }
    }

I hope this gives you some ideas for developing robust payment options for your site and provide a nice user experience for users Smile

Remember to also deal with your dates in UTC format, and ensure the date kind property is set to UTC as well for extra safety when storing the transaction date.

So in a nutshell, you can have top notch validation that protects from spoofing attacks if you ALWAYS take a IPN and PDT and compare it with the original transaction object! If you keep your transaction id’s unpredictable, users can never guess someone else  transactionid and hijack it for payment. So you can have a nice invoice number e.g. 90124, but the transactionid is not easy to predict, it could be a Guid etc.

 

Imagine if the above was not the case a smart user could then create two transactions, one for a cheap item and one for an expensive one, here can then hijack the ipn/pdt, or send a FAKE pdt/ipn to your system, and then swap the item information around, and then later cancel the expensive item transaction, however, he hijacked the cheap item and changed the item list and transactionid, hence why I say, check AMOUNT, CURRENCY, Items, TranactionID etc. There are allot of sites out there that are easy to hack due to them not doing these sort of double checks on Ipn and Pdt messages from Paypal. If I know you IPN or PDT listener, I can send fake messages in and try guess weaknesses by using methods such as generating two transactions on the site and trying to swap items around, currencies, etc In fact, because they cannot guess my transaction number, I do not need to check items in the list, as this is not processed in my listener, so if they changed a flower pot to a BMW, who cares, I ignore this sort of data in an IPN/PDT, as long as the fundamentals are the same we in good shape.

Matthew Will and I spent allot of time think these sort of scenarios through and nutted out a nice secure solution. The above samples should get you started in the right direction for Paypal Standard integration. Allot of implementation logic is left out, and this is done on purpose.

Cheers

Linq to SQL Anti-Patterns–Dealing with nullable types

Hi Guys,

This blog will demonstrate some bad habits in LinqToSql and how to deal with nullable types that provide clean code without null checks all over and improved performance on the projections.

In one of my posts I recommended and demonstrated value of using NHProf, well the same goes for Linq to SQL. You can download a trial here:

http://l2sprof.com/

It is really easy to use, the the case of ASP.NET, just add a global.asax or edit an existing one’s code behind like so:

public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {
            try
            {

                var profileOn = false;
                bool.TryParse(ConfigurationManager.AppSettings["EnableLinqToSQLProfile"], out profileOn);
                if (profileOn)
                    HibernatingRhinos.Profiler.Appender.LinqToSql.LinqToSqlProfiler.Initialize();
            }
            catch (Exception)
            {
               Debug.WriteLine("Could not initialize LinqToSql Profiling");
            }
        }

Excellent, then add a reference to the HibernatingRhinos.Profiler.Appender.dll which I guess can be in your lib folder.

Right, now lets have a look at some customer code that is causing 2 hits to the database, which we can reduce it to one hit.

Old Code causing 2 hits:

 public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = _db.CaseStudies.Where(i => i.PageID == pageID);

            if (result.Any())
            {
                return result.First().CaseStudyId;
            }

            return -1;
        }

So from above, if we attach the L2SQL profiler, we will see 2 exact same queries going to the DB, one for the .Any() and then the other for the c.First.

image

Useful Query: (Note the extra columns we do not need, we come back to this, as I do not like the projection here, too many columns, I just need the PageID!!)

SELECT TOP ( 1 ) [t0].[CaseStudyId],
                 [t0].[PageID],
                 [t0].[Title],
                 [t0].[ShortDescription],
                 [t0].[LongDescription],
                 [t0].[Challenge],
                 [t0].[Solution],
                 [t0].[Results],
                 [t0].[ImageURL],
                 [t0].[Rank],
                 [t0].[Visible],
                 [t0].[ModifiedById],
                 [t0].[DateCreated],
                 [t0].[DateModified]
FROM   [dbo].[CaseStudy] AS [t0]
WHERE  [t0].[PageID] = 128 /* @p0 */

So the extra query is:

SELECT (CASE 
          WHEN EXISTS (SELECT NULL AS [EMPTY]
                       FROM   [dbo].[CaseStudy] AS [t0]
                       WHERE  [t0].[PageID] = 128 /* @p0 */) THEN 1
          ELSE 0
        END) AS [value]

Lets Improve it:

        public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = _db.CaseStudies.FirstOrDefault(i => i.PageID == pageID);
            return result == null ? -1 : result.CaseStudyId;
        }

Now in the profiler, we will see only 1 statement being executed, as we removed the Any() extension method.

image

The same goes for counts

Old code – 2 queries to the DB:

public int GetCaseStudyIDByPageID(int pageID)
        {
            var r = from I in DB.CaseStudies
                    where I.PageID == pageID
                    select I.CaseStudyId;

            return r.Count() == 0 ? -1 : r.First();
        }

 

New optimised code:

      public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = _db.CaseStudies.FirstOrDefault(i => i.PageID == pageID);
            return result == null ? -1 : result.CaseStudyId;
        }

 

Let’s improve it further by using projections to reduce the number of columns coming back, if you look at the result, we get all the columns back, which is extra data over the wire:

        public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = _db.CaseStudies.Where(z => z.PageID == pageID).Select(z => (int?)z.CaseStudyId).FirstOrDefault();
            return result ?? -1;

        }
SELECT TOP ( 1 ) [t1].[value]
FROM   (SELECT [t0].[CaseStudyId] AS [value],
               [t0].[PageID]
        FROM   [dbo].[CaseStudy] AS [t0]) AS [t1]
WHERE  [t1].[PageID] = 128 /* @p0 */

This is much less columns being returned.

image

However, we can improve this further by creating a method to handle this for us, how about something along the lines of NullableFirstOrDefault….

        public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = NullableFirstOrDefault(_db.CaseStudies.Where(z => z.PageID == pageID).Select(z => z.CaseStudyId));
            return result ?? -1;

        }


        public Nullable<T> NullableFirstOrDefault<T>(IQueryable<T> input) where T : struct
        {
            return input.Select(z => (Nullable<T>)z).FirstOrDefault();
        }

Now, we get the same result, with limited projection over the wire, but we can then use this as an extension method to optimise all nullable  first or defaults.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ROMIKO.NET.LINQ.Extensions
{
    public static class LinqToSqlExtension
    {


        public static Nullable<T> NullableFirstOrDefault<T>(this IQueryable<T> input) where T : struct
        {
            return input.Select(z => (Nullable<T>)z).FirstOrDefault();
        }

        public static IQueryable<Nullable<T>> SelectNullable<T>(this IQueryable<T> input) where T : struct
        {
            return input.Select(z => (Nullable<T>)z);
        }
    }
}

and now, we can use it like this, without any null checks

        public int GetCaseStudyIDByPageID(int pageID)
        {
            var result = _db.CaseStudies.Where(z => z.PageID == pageID).Select(z => z.CaseStudyId).NullableFirstOrDefault();
            return result ?? -1;

        }

 

The result is the same, but easier to read code and faster queries Smile

Lets take it a step further and make it even easier to read the code by introducing extension methods that allows you to provide default values for nullable types (Matthew, you a geek!)

        public static T FirstOrDefault<T>(this IQueryable<T> input, T defaultValue) where T : struct
        {
            return input.Select(z => (Nullable<T>)z).FirstOrDefault() ?? defaultValue;
        }

So our LINQ extension class looks like this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MATTHEWWILLS.NET.LINQ.Extensions
{
    public static class LinqToSqlExtension
    {


        public static Nullable<T> NullableFirstOrDefault<T>(this IQueryable<T> input) where T : struct
        {
            return input.Select(z => (Nullable<T>)z).FirstOrDefault();
        }

        public static T FirstOrDefault<T>(this IQueryable<T> input, T defaultValue) where T : struct
        {
            return input.Select(z => (Nullable<T>)z).FirstOrDefault() ?? defaultValue;
        }


        public static IQueryable<Nullable<T>> SelectNullable<T>(this IQueryable<T> input) where T : struct
        {
            return input.Select(z => (Nullable<T>)z);
        }
    }
}

Nice, now look how easy the code is to read and it is optimised on projects.

 

      public int GetCaseStudyIDByPageID(int pageID)
        {
            return _db.CaseStudies.Where(z => z.PageID == pageID).Select(z => z.CaseStudyId).FirstOrDefault(-1);
        }

The above produces the same result in the profiler:

SELECT TOP ( 1 ) [t1].[value]
FROM   (SELECT [t0].[CaseStudyId] AS [value],
               [t0].[PageID]
        FROM   [dbo].[CaseStudy] AS [t0]) AS [t1]
WHERE  [t1].[PageID] = 128 /* @p0 */

This anti pattern was used allot throughout their code, and caused double/triple calls to the DB for every page load. Therefore, using a profiling tool like NHProf, L2SProf or EHProf will save you and your customer/employee allot of money in the long term and perhaps save developers from picking up bad habits where IQuerable is being abused and treated like lists when in fact they execute on the backend.

We have solved this, and also provided a neat way of dealing with nullable types with  clean extension methods.

So we have solved scalar issues with FirstOrDefault and the code below which is easy to write to the untrained eye will not need to be thought of when invalid checks occur on nullable types by using the custom extension methods provided above.

var result = _db.CaseStudies.FirstOrDefault(i => i.PageID == pageID).Select(x=>x.PageId);

return result ?? -1; //Invalid Check

Also, see the repository pattern being used and people ended up with code with these stats:

clip_image002

Above, we have +- 700 SQL Statements being called, and each on average uses a data context. Try to have a minimum amount of data contexts. One Data Context can server all sorts of requests, so when choosing a repository pattern or strategy profile the number of data contexts created and try reduce them.

image

Above, is for one page load, not a good design pattern, so room to improve with some dependency injection and a singleton on the datacontext Smile

You can read other tips on profiling here:

https://romikoderbynew.wordpress.com/tag/nhprof/

Thanks to Matthew Wills again for some awesome tips on profiling Smile

Cheers

Memory Dump Analysis–W3WP IIS Process

At a customer I had prepared some Visual Studio 2010 WebTests which were calling their Java based website hosted on IBM Websphere, there is an IFrame on the Java page that points to an IIS hosted Asp.net 2.0 web site.

When running load tests I noticed that the webtests were taking a very long time to execute at one point the load test grinded the servers to a halt and a colleague of mine Tatham Oddie decided it was time we dump the memory from the IIS Process hosting the site.

image

So, right click the w3wp.exe process and click Create Dump File.

We also have windbg installed, which you can download from

http://www.windbg.org/

Ensure you click debugging tools for windows under redistributable, so you get 32/64 bit versions

image

So, the big picture is for us to look at what is on the stack, ideally in software we should have items living on the stack for a very short period of time, so lets analyze the stack and see what clues it might have about the web site behaving badly.

Open Windbg, it is located in

C:\Program Files\Debugging Tools for Windows (x64) or you might have the 32 bit version.

I also copied the following files

w3wp.dmp (the generated dump

mscordacwks.dll (From Server hosting the IIS service)

mscordacwks_64.dll (From Server hosting the IIS service)

image

Start windbg and open the dump file.

image

excellent, we are presented with

.image

SOS will need the Data Access Layer to make calls into the CLR, so we need the correct version of the mscordacwks.dll file from the Server where the software was running. This can be either 32 bit or 64 bit.

They are located at:

C:\Windows\Microsoft.NET\Framework\v2.0.50727

C:\Windows\Microsoft.NET\Framework64\v2.0.50727

Now, we need to tell the debugger where this file is, I copied these files and the dmp file from the server and put it into my E:\projects\debug folder

first, lets get the correct mscordacwks loaded, I renamed my 64bit version to this name:

mscordacwks_AMD64_AMD64_2.0.50727.5420.dll you can find the file version in the property page for mscordacwks_64

I then point my debugger to the renamed version of mscordackwks.

.sympath+ e:\projects\debug

then I load the DAC

.cordll –ve –u –l

I can now load the sos commands

.loadby sos mscorwks

if you have issues load the mscordacwks, you can also try

!sym noisy
.symfix e:\projects\debug
.cordll -ve -u -l

this will then try load the symbols from the microsoft server (http://msdl.microsoft.com/download/symbols), you must have an internet connection.

Lets see what threads are in the process:

0:000> !threads
ThreadCount: 37
UnstartedThread: 0
BackgroundThread: 10
PendingThread: 0
DeadThread: 27
Hosted Runtime: no
                                              PreEmptive                                                Lock
       ID OSID        ThreadOBJ     State   GC     GC Alloc Context                  Domain           Count APT Exception
   4    1 1378 00000000017fb730      8220 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn
  12    2  970 00000000017dd1d0      b220 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 MTA (Finalizer)
  13    4 107c 000000001a9c8cf0    80a220 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 MTA (Threadpool Completion Port)
  14    5 12d0 000000001a9d5fc0      1220 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn
XXXX    6    0 000000001aa36200   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 MTA (Threadpool Worker)
XXXX    8    0 000000001aaac0b0      9820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn
XXXX    9    0 000000001aaab850   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX    a    0 000000001ca2b840      9820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn
XXXX    b    0 000000001caa3830   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX    c    0 000000001ab937c0   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX    d    0 000000001cab4680   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX    e    0 000000001ca52940   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX    f    0 000000001caaf450   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   10    0 000000001ca57290   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   11    0 000000001cab5c60   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 MTA (Threadpool Worker)
XXXX   12    0 000000001aa52ca0   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   13    0 000000001aa53270   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
   2   14  f48 000000001aa53840       220 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn
XXXX   15    0 000000001aa543e0   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
  17   16  834 000000001aa549b0   180b220 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 MTA (Threadpool Worker)
XXXX   17    0 000000001aa54f80      9820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn
XXXX   18    0 000000001aa55550   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 MTA (Threadpool Worker)
XXXX   19    0 000000001aa55b20   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   1a    0 000000001aa560f0   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   1b    0 000000001aa566c0   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   1c    0 000000001aa56c90   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   1d    0 000000001aa57260   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   1e    0 000000001aa57830   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   1f    0 000000001aa57e00   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   20    0 000000001aa583d0   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
  18   21 121c 000000001aa589a0   380b220 Enabled  0000000000000000:0000000000000000 000000001ab979c0     1 MTA (Threadpool Worker)
XXXX   22    0 000000001aa58f70   1801820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn (Threadpool Worker)
XXXX   24    0 000000001aa59b10      9820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn
XXXX   26    0 000000001ef10680      9820 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 Ukn
  19   25 11bc 000000001ef100b0   200b220 Enabled  0000000000000000:0000000000000000 000000001ab979c0     1 MTA
  21    3 13c8 000000001aa526d0   880b220 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 MTA (Threadpool Completion Port)
  22    7 12f4 000000001aa53e10   880b220 Enabled  0000000000000000:0000000000000000 00000000017ea4f0     0 MTA (Threadpool Completion Port)

Ok, from this we will need to try find the thread that is running all the IIS web pages etc. SO lets execute

0:000> !eestack
———————————————
Thread   4
Child-SP         RetAddr          Call Site
0000000001a2f7a8 000007fefd47169d ntdll!NtRemoveIoCompletion+0xa
0000000001a2f7b0 000000007731a4e1 KERNELBASE!GetQueuedCompletionStatus+0x3d
0000000001a2f810 000007fef9e81f7b kernel32!GetQueuedCompletionStatusStub+0x11
0000000001a2f850 000007fef9e82024 w3tp!THREAD_POOL_DATA::ThreadPoolThread+0x3b
0000000001a2f8a0 000007fef9e820a1 w3tp!THREAD_POOL_DATA::ThreadPoolThread+0x34
0000000001a2f8d0 000000007732652d w3tp!THREAD_MANAGER::ThreadManagerThread+0x61
0000000001a2f900 000000007745c521 kernel32!BaseThreadInitThunk+0xd
0000000001a2f930 0000000000000000 ntdll!RtlUserThreadStart+0x21
———————————————
Thread  12
Child-SP         RetAddr          Call Site
000000001a9af5e8 000007fefd471420 ntdll!ZwWaitForMultipleObjects+0xa
000000001a9af5f0 0000000077321220 KERNELBASE!GetCurrentProcess+0x40
000000001a9af6f0 000007fef82a0353 kernel32!WaitForMultipleObjects+0xb0
000000001a9af780 000007fef8297b9a mscorwks!WKS::WaitForFinalizerEvent+0x93
000000001a9af7b0 000007fef8295a3c mscorwks!WKS::GCHeap::FinalizerThreadWorker+0x4a
000000001a9af7f0 000007fef827ba45 mscorwks!CClosedHashEx<CCustAttrHashKey,CCustAttrHash>::Hash+0x30
000000001a9af840 000007fef81735b5 mscorwks!SVR::gc_heap::make_heap_segment+0x155
000000001a9af910 000007fef829837a mscorwks!EEToProfilerExceptionInterfaceWrapper::ExceptionCatcherEnter+0x4d
000000001a9af950 000007fef82979b4 mscorwks!ManagedThreadBase_NoADTransition+0x42
000000001a9af9b0 000007fef8298164 mscorwks!WKS::GCHeap::FinalizerThreadStart+0x74
000000001a9af9f0 000000007732652d mscorwks!Thread::intermediateThreadProc+0x78
000000001a9afac0 000000007745c521 kernel32!BaseThreadInitThunk+0xd
000000001a9afaf0 0000000000000000 ntdll!RtlUserThreadStart+0x21
———————————————
Thread  13
Child-SP         RetAddr          Call Site
000000001a8afd28 000007fefd471203 ntdll!NtDelayExecution+0xa
000000001a8afd30 000007fef814812d KERNELBASE!SleepEx+0xb3
000000001a8afdd0 000007fef82a1d67 mscorwks!ThreadpoolMgr::TimerThreadFire+0x51
000000001a8afe70 000000007732652d mscorwks!ThreadpoolMgr::TimerThreadStart+0x6b
000000001a8afeb0 000000007745c521 kernel32!BaseThreadInitThunk+0xd
000000001a8afee0 0000000000000000 ntdll!RtlUserThreadStart+0x21
———————————————
Thread  14
Child-SP         RetAddr          Call Site
000000001adcf368 000007fefd4710ac ntdll!NtWaitForSingleObject+0xa
000000001adcf370 000007fef81cb540 KERNELBASE!WaitForSingleObjectEx+0x9c
000000001adcf410 000007fef81cb42f mscorwks!CLREvent::WaitEx+0x174
000000001adcf460 000007fef82b1a9f mscorwks!CLREvent::WaitEx+0x63
000000001adcf510 000007fef8298164 mscorwks!AppDomain::ADUnloadThreadStart+0x153
000000001adcf630 000000007732652d mscorwks!Thread::intermediateThreadProc+0x78
000000001adcf780 000000007745c521 kernel32!BaseThreadInitThunk+0xd
000000001adcf7b0 0000000000000000 ntdll!RtlUserThreadStart+0x21
———————————————
Thread   2
Child-SP         RetAddr          Call Site
0000000000b3f578 000000007744fe0b ntdll!ZwWaitForWorkViaWorkerFactory+0xa
0000000000b3f580 000000007732652d ntdll!RtlValidateHeap+0x3bb
0000000000b3f880 000000007745c521 kernel32!BaseThreadInitThunk+0xd
0000000000b3f8b0 0000000000000000 ntdll!RtlUserThreadStart+0x21
———————————————
Thread  17
Child-SP         RetAddr          Call Site
000000001ea0ed78 000007fefd4710ac ntdll!NtWaitForSingleObject+0xa
000000001ea0ed80 000007fef81cb540 KERNELBASE!WaitForSingleObjectEx+0x9c
000000001ea0ee20 000007fef81cb42f mscorwks!CLREvent::WaitEx+0x174
000000001ea0ee70 000007fef81cb18b mscorwks!CLREvent::WaitEx+0x63
000000001ea0ef20 000007fef81d841a mscorwks!ThreadpoolMgr::SafeWait+0x7b
000000001ea0efe0 000007fef8298164 mscorwks!ThreadpoolMgr::WorkerThreadStart+0x11a
000000001ea0f080 000000007732652d mscorwks!Thread::intermediateThreadProc+0x78
000000001ea0fad0 000000007745c521 kernel32!BaseThreadInitThunk+0xd
000000001ea0fb00 0000000000000000 ntdll!RtlUserThreadStart+0x21
———————————————
Thread  18
Child-SP         RetAddr          Call Site
000000001e88d578 000007fefd471420 ntdll!ZwWaitForMultipleObjects+0xa
000000001e88d580 0000000077332d53 KERNELBASE!GetCurrentProcess+0x40
000000001e88d680 000007fef817212d kernel32!WaitForMultipleObjectsExImplementation+0xb3
000000001e88d710 000007fef81769b9 mscorwks!WaitForMultipleObjectsEx_SO_TOLERANT+0xc1
000000001e88d7b0 000007fef8277cf9 mscorwks!Thread::DoAppropriateAptStateWait+0x41
000000001e88d810 000007fef8192b5c mscorwks!Thread::DoAppropriateWaitWorker+0x191
000000001e88d910 000007fef87133c1 mscorwks!Thread::DoAppropriateWait+0x5c
000000001e88d980 000007fef36c6653 mscorwks!WaitHandleNative::CorWaitOneNative+0x221
000000001e88dbc0 000007fef2b3e817 mscorlib_ni!System.Threading.WaitHandle.WaitOne(Int64, Boolean)+0x23
000000001e88dc00 000007fef2b48641 System_ni!System.Net.LazyAsyncResult.WaitForCompletion(Boolean)+0xc7
000000001e88dc60 000007fef2b47f34 System_ni!System.Net.Connection.SubmitRequest(System.Net.HttpWebRequest)+0x2d1
000000001e88dcf0 000007fef2b474f8 System_ni!System.Net.ServicePoint.SubmitRequest(System.Net.HttpWebRequest, System.String)+0x84
000000001e88dd60 000007fef2b4b031 System_ni!System.Net.HttpWebRequest.SubmitRequest(System.Net.ServicePoint)+0x268
000000001e88ddc0 000007ff00705bbb System_ni!System.Net.HttpWebRequest.GetResponse()+0x291
000000001e88de50 000007fef09da30a RomikoCommon_72a00000!RomikoCommon.PageBase.OnPreInit(System.EventArgs)+0x36b
000000001e88df00 000007fef09dadb8 System_Web_ni!System.Web.UI.Page.PerformPreInit()+0x2a
000000001e88df30 000007fef09da750 System_Web_ni!System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)+0x4e8
000000001e88e000 000007fef09da67b System_Web_ni!System.Web.UI.Page.ProcessRequest(Boolean, Boolean)+0xa0
000000001e88e060 000007fef09da610 System_Web_ni!System.Web.UI.Page.ProcessRequest()+0x5b
000000001e88e0c0 000007ff007049f4 System_Web_ni!System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)+0xf0
000000001e88e120 000007fef09e1ab7 App_Web_d_5hdouw!ASP.policysearch_aspx.ProcessRequest(System.Web.HttpContext)+0x34
000000001e88e150 000007fef09a571b System_Web_ni!System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()+0x257
000000001e88e200 000007fef1090561 System_Web_ni!System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)+0xab
000000001e88e2a0 000007fef10811c2 System_Web_ni!System.Web.HttpApplication+PipelineStepManager.ResumeSteps(System.Exception)+0x511
000000001e88e430 000007fef1062df9 System_Web_ni!System.Web.HttpApplication.BeginProcessRequestNotification(System.Web.HttpContext, System.AsyncCallback)+0x72
000000001e88e480 000007fef1189931 System_Web_ni!System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.Hosting.IIS7WorkerRequest, System.Web.HttpContext)+0x269
000000001e88e5a0 000007fef1189d8b System_Web_ni!System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32)+0x411
000000001e88e720 000007fef1189294 System_Web_ni!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32)+0x2b
000000001e88e780 000007fef831f18a System_Web_ni!DomainNeutralILStubClass.IL_STUB(Int64, Int64, Int64, Int32)+0x24
000000001e88e7c0 000007fef7dd51a7 mscorwks!UMThunkStubAMD64+0x7a
000000001e88e850 000007fef7dd6262 webengine!RegisterModule+0x1e87
000000001e88e8a0 000007fef7dd1ade webengine!GetEcb+0x3c2
000000001e88e910 000007fef7dd2114 webengine!CSpinLock::ConvertSharedToExclusive+0x129e
000000001e88e9a0 000007fef8012de7 webengine!CSpinLock::ConvertSharedToExclusive+0x18d4
000000001e88e9e0 000007fef80146a4 iiscore!NOTIFICATION_CONTEXT::RequestDoWork+0x233
000000001e88ea20 000007fef801a775 iiscore!NOTIFICATION_CONTEXT::CallModulesInternal+0x174
000000001e88eb10 000007fef8015a03 iiscore!NOTIFICATION_CONTEXT::CallModules+0x25
000000001e88eb60 000007fef801a81c iiscore!W3_CONTEXT::DoWork+0x34d
000000001e88ee80 000007fef7dcfc41 iiscore!W3_CONTEXT::IndicateCompletion+0x8c
000000001e88eee0 000007fef8320e37 webengine!MgdIndicateCompletion+0x61
000000001e88ef10 000007fef108b25b mscorwks!DoNDirectCall__PatchGetThreadCall+0x7b
000000001e88efb0 000007fef1189a60 System_Web_ni!DomainNeutralILStubClass.IL_STUB(IntPtr, System.Web.RequestNotificationStatus ByRef)+0x4b
000000001e88f090 000007fef1189d8b System_Web_ni!System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32)+0x540
000000001e88f210 000007fef1189294 System_Web_ni!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32)+0x2b
000000001e88f270 000007fef831f3db System_Web_ni!DomainNeutralILStubClass.IL_STUB(Int64, Int64, Int64, Int32)+0x24
000000001e88f2b0 000007fef830d079 mscorwks!UM2MThunk_WrapperHelper+0x4b
000000001e88f2f0 000007fef830d165 mscorwks!UM2MThunk_WrapperWorker+0x99
000000001e88f380 000007fef8295bc9 mscorwks!UM2MThunk_Wrapper+0x19
000000001e88f3b0 000007fef82fe398 mscorwks!Thread::DoADCallBack+0x145
000000001e88f520 000007fef831f386 mscorwks!UM2MDoADCallBack+0x90
000000001e88f5b0 000007fef7dd51a7 mscorwks!UMThunkStubAMD64+0x276
000000001e88f640 000007fef7dd1066 webengine!RegisterModule+0x1e87
000000001e88f690 000007fef81c56f7 webengine!CSpinLock::ConvertSharedToExclusive+0x826
000000001e88f6c0 000007fef81d84ba mscorwks!UnManagedPerAppDomainTPCount::DispatchWorkItem+0x157
000000001e88f760 000007fef8298164 mscorwks!ThreadpoolMgr::WorkerThreadStart+0x1ba
000000001e88f800 000000007732652d mscorwks!Thread::intermediateThreadProc+0x78
000000001e88fdd0 000000007745c521 kernel32!BaseThreadInitThunk+0xd
000000001e88fe00 0000000000000000 ntdll!RtlUserThreadStart+0x21
———————————————
Thread  19
Child-SP         RetAddr          Call Site
000000001b5ed648 000007fefd471420 ntdll!ZwWaitForMultipleObjects+0xa
000000001b5ed650 0000000077332d53 KERNELBASE!GetCurrentProcess+0x40
000000001b5ed750 000007fef817212d kernel32!WaitForMultipleObjectsExImplementation+0xb3
000000001b5ed7e0 000007fef81769b9 mscorwks!WaitForMultipleObjectsEx_SO_TOLERANT+0xc1
000000001b5ed880 000007fef8277cf9 mscorwks!Thread::DoAppropriateAptStateWait+0x41
000000001b5ed8e0 000007fef8192b5c mscorwks!Thread::DoAppropriateWaitWorker+0x191
000000001b5ed9e0 000007fef8712f21 mscorwks!Thread::DoAppropriateWait+0x5c
000000001b5eda50 000007fef3687e24 mscorwks!WaitHandleNative::CorWaitMultipleNative+0x2d1
000000001b5edcd0 000007fef2b11a67 mscorlib_ni!System.Threading.WaitHandle.WaitAny(System.Threading.WaitHandle[], Int32, Boolean)+0x64
000000001b5edd30 000007fef366dd38 System_ni!System.Net.TimerThread.ThreadProc()+0x327
000000001b5ede00 000007fef8321612 mscorlib_ni!System.Threading.ExecutionContext.runTryCode(System.Object)+0x178
000000001b5edec0 000007fef824ee13 mscorwks!CallDescrWorker+0x82
000000001b5edf10 000007fef86fbc51 mscorwks!CallDescrWorkerWithHandler+0xd3
000000001b5edfb0 000007fef82456f2 mscorwks!MethodDesc::CallDescr+0x2b1
000000001b5ee200 000007fef877b182 mscorwks!ExecuteCodeWithGuaranteedCleanupHelper+0x12a
000000001b5ee490 000007fef3652b82 mscorwks!ReflectionInvocation::ExecuteCodeWithGuaranteedCleanup+0x172
000000001b5ee6a0 000007fef36ea91d mscorlib_ni!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)+0x62
000000001b5ee6f0 000007fef8321612 mscorlib_ni!System.Threading.ThreadHelper.ThreadStart()+0x4d
000000001b5ee740 000007fef824ee13 mscorwks!CallDescrWorker+0x82
000000001b5ee790 000007fef86fbc51 mscorwks!CallDescrWorkerWithHandler+0xd3
000000001b5ee830 000007fef8137a7d mscorwks!MethodDesc::CallDescr+0x2b1
000000001b5eea70 000007fef8295a3c mscorwks!ThreadNative::KickOffThread_Worker+0x191
000000001b5eed90 000007fef827ba45 mscorwks!CClosedHashEx<CCustAttrHashKey,CCustAttrHash>::Hash+0x30
000000001b5eede0 000007fef81735b5 mscorwks!SVR::gc_heap::make_heap_segment+0x155
000000001b5eeeb0 000007fef82a277d mscorwks!EEToProfilerExceptionInterfaceWrapper::ExceptionCatcherEnter+0x4d
000000001b5eeef0 000007fef8295bc9 mscorwks!SharedFileLockHolderBase::DoRelease+0x4d
000000001b5eef20 000007fef8295a61 mscorwks!Thread::DoADCallBack+0x145
000000001b5ef090 000007fef827ba45 mscorwks!CClosedHashEx<CCustAttrHashKey,CCustAttrHash>::Hash+0x55
000000001b5ef0e0 000007fef81735b5 mscorwks!SVR::gc_heap::make_heap_segment+0x155
000000001b5ef1b0 000007fef82e05e9 mscorwks!EEToProfilerExceptionInterfaceWrapper::ExceptionCatcherEnter+0x4d
000000001b5ef1f0 000007fef829ffbb mscorwks!ZapNibbleMap::GetSize+0x55
000000001b5ef250 000007fef8298164 mscorwks!ThreadNative::KickOffThread+0xd3
000000001b5ef330 000000007732652d mscorwks!Thread::intermediateThreadProc+0x78
000000001b5ef880 000000007745c521 kernel32!BaseThreadInitThunk+0xd
000000001b5ef8b0 0000000000000000 ntdll!RtlUserThreadStart+0x21
———————————————
Thread  21
Child-SP         RetAddr          Call Site
000000001de2eed8 000007fefd47169d ntdll!NtRemoveIoCompletion+0xa
000000001de2eee0 000000007731a4e1 KERNELBASE!GetQueuedCompletionStatus+0x3d
000000001de2ef40 000007fef82b347a kernel32!GetQueuedCompletionStatusStub+0x11
000000001de2ef80 000007fef8298164 mscorwks!ThreadpoolMgr::CompletionPortThreadStart+0xee
000000001de2f020 000000007732652d mscorwks!Thread::intermediateThreadProc+0x78
000000001de2f7f0 000000007745c521 kernel32!BaseThreadInitThunk+0xd
000000001de2f820 0000000000000000 ntdll!RtlUserThreadStart+0x21
———————————————
Thread  22
Child-SP         RetAddr          Call Site
000000001e07eed8 000007fefd47169d ntdll!NtRemoveIoCompletion+0xa
000000001e07eee0 000000007731a4e1 KERNELBASE!GetQueuedCompletionStatus+0x3d
000000001e07ef40 000007fef82b347a kernel32!GetQueuedCompletionStatusStub+0x11
000000001e07ef80 000007fef8298164 mscorwks!ThreadpoolMgr::CompletionPortThreadStart+0xee
000000001e07f020 000000007732652d mscorwks!Thread::intermediateThreadProc+0x78
000000001e07f870 000000007745c521 kernel32!BaseThreadInitThunk+0xd
000000001e07f8a0 0000000000000000 ntdll!RtlUserThreadStart+0x21

Thread 18 looks like a good candidate, we dealing with ASP>NET and we can see page loads etc, in fact a Page_Init is on the stack in that thread and a web request, also, the page I was testing was indeed a policysearch which had crashed!

000000001e88e120 000007fef09e1ab7 App_Web_d_5hdouw!ASP.policysearch_aspx.ProcessRequest

So, we on the right track, lets activate this as the active thread

we will then switch threads by typing ~18s, now we can load the stack as it is managed code.

0:000> ~18s
ntdll!ZwWaitForMultipleObjects+0xa:
00000000`774818ca c3              ret
0:018> !clrstack
OS Thread Id: 0x121c (18)
Child-SP         RetAddr          Call Site
000000001e88dbc0 000007fef2b3e817 System.Threading.WaitHandle.WaitOne(Int64, Boolean)
000000001e88dc00 000007fef2b48641 System.Net.LazyAsyncResult.WaitForCompletion(Boolean)
000000001e88dc60 000007fef2b47f34 System.Net.Connection.SubmitRequest(System.Net.HttpWebRequest)
000000001e88dcf0 000007fef2b474f8 System.Net.ServicePoint.SubmitRequest(System.Net.HttpWebRequest, System.String)
000000001e88dd60 000007fef2b4b031 System.Net.HttpWebRequest.SubmitRequest(System.Net.ServicePoint)
000000001e88ddc0 000007ff00705bbb System.Net.HttpWebRequest.GetResponse()
000000001e88de50 000007fef09da30a RomikoCommon.PageBase.OnPreInit(System.EventArgs)
000000001e88df00 000007fef09dadb8 System.Web.UI.Page.PerformPreInit()
000000001e88df30 000007fef09da750 System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
000000001e88e000 000007fef09da67b System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
000000001e88e060 000007fef09da610 System.Web.UI.Page.ProcessRequest()
000000001e88e0c0 000007ff007049f4 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)
000000001e88e120 000007fef09e1ab7 ASP.policysearch_aspx.ProcessRequest(System.Web.HttpContext)
000000001e88e150 000007fef09a571b System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
000000001e88e200 000007fef1090561 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
000000001e88e2a0 000007fef10811c2 System.Web.HttpApplication+PipelineStepManager.ResumeSteps(System.Exception)
000000001e88e430 000007fef1062df9 System.Web.HttpApplication.BeginProcessRequestNotification(System.Web.HttpContext, System.AsyncCallback)
000000001e88e480 000007fef1189931 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.Hosting.IIS7WorkerRequest, System.Web.HttpContext)
000000001e88e5a0 000007fef1189d8b System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32)
000000001e88e720 000007fef1189294 System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32)
000000001e88e780 000007fef831f18a DomainNeutralILStubClass.IL_STUB(Int64, Int64, Int64, Int32)
000000001e88efb0 000007fef1189a60 DomainNeutralILStubClass.IL_STUB(IntPtr, System.Web.RequestNotificationStatus ByRef)
000000001e88f090 000007fef1189d8b System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32)
000000001e88f210 000007fef1189294 System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32)
000000001e88f270 000007fef831f3db DomainNeutralILStubClass.IL_STUB(Int64, Int64, Int64, Int32)

Interesting, we can see a SibmitRequest on the stack which is a synchronous call to a web page, so this web page is called another web page, and is waiting for a response. Lets get more info about this item:

000000001e88dc60 000007fef2b47f34 System.Net.Connection.SubmitRequest(System.Net.HttpWebRequest)

lets get the paramters etc with the –p option for the clrstack

0:018> !clrstack -p
OS Thread Id: 0x121c (18)
Child-SP         RetAddr          Call Site
000000001e88dbc0 000007fef2b3e817 System.Threading.WaitHandle.WaitOne(Int64, Boolean)
    PARAMETERS:
        this = <no data>
        timeout = <no data>
        exitContext = <no data>

000000001e88dc00 000007fef2b48641 System.Net.LazyAsyncResult.WaitForCompletion(Boolean)
    PARAMETERS:
        this = 0x00000000033d4778
        snap = <no data>

000000001e88dc60 000007fef2b47f34 System.Net.Connection.SubmitRequest(System.Net.HttpWebRequest)
    PARAMETERS:
        this = <no data>
        request = <no data>

000000001e88dcf0 000007fef2b474f8 System.Net.ServicePoint.SubmitRequest(System.Net.HttpWebRequest, System.String)
    PARAMETERS:
        this = <no data>
        request = <no data>
        connName = <no data>

000000001e88dd60 000007fef2b4b031 System.Net.HttpWebRequest.SubmitRequest(System.Net.ServicePoint)
    PARAMETERS:
        this = 0x00000000033d3b48
        servicePoint = <no data>

000000001e88ddc0 000007ff00705bbb System.Net.HttpWebRequest.GetResponse()
    PARAMETERS:
        this = <no data>

000000001e88de50 000007fef09da30a RomikoCommon.PageBase.OnPreInit(System.EventArgs)
    PARAMETERS:
        this = <no data>
        e = <no data>

000000001e88df00 000007fef09dadb8 System.Web.UI.Page.PerformPreInit()
    PARAMETERS:
        this = <no data>

000000001e88df30 000007fef09da750 System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
    PARAMETERS:
        this = 0x00000000033adfc8
        includeStagesBeforeAsyncPoint = 0x0000000000000001
        includeStagesAfterAsyncPoint = 0x0000000000000001

000000001e88e000 000007fef09da67b System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
    PARAMETERS:
        this = 0x00000000033adfc8
        includeStagesBeforeAsyncPoint = <no data>
        includeStagesAfterAsyncPoint = 0x0000000000000001

000000001e88e060 000007fef09da610 System.Web.UI.Page.ProcessRequest()
    PARAMETERS:
        this = 0x00000000033adfc8

000000001e88e0c0 000007ff007049f4 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)
    PARAMETERS:
        this = <no data>
        context = <no data>

000000001e88e120 000007fef09e1ab7 ASP.policysearch_aspx.ProcessRequest(System.Web.HttpContext)
    PARAMETERS:
        this = 0x00000000033adfc8
        context = 0x00000000033acc28

000000001e88e150 000007fef09a571b System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
    PARAMETERS:
        this = <no data>

000000001e88e200 000007fef1090561 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
    PARAMETERS:
        this = 0x000000000322d088
        step = 0x0000000003230990
        completedSynchronously = 0x000000001e88e359

000000001e88e2a0 000007fef10811c2 System.Web.HttpApplication+PipelineStepManager.ResumeSteps(System.Exception)
    PARAMETERS:
        this = 0x00000000032307c8
        error = <no data>

000000001e88e430 000007fef1062df9 System.Web.HttpApplication.BeginProcessRequestNotification(System.Web.HttpContext, System.AsyncCallback)
    PARAMETERS:
        this = <no data>
        context = <no data>
        cb = <no data>

000000001e88e480 000007fef1189931 System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.Hosting.IIS7WorkerRequest, System.Web.HttpContext)
    PARAMETERS:
        this = 0x0000000002579188
        wr = 0x00000000033ac938
        context = 0x00000000033acc28

000000001e88e5a0 000007fef1189d8b System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32)
    PARAMETERS:
        managedHttpContext = <no data>
        nativeRequestContext = 0x000000001cd7fc58
        moduleData = <no data>
        flags = <no data>

000000001e88e720 000007fef1189294 System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32)
    PARAMETERS:
        managedHttpContext = <no data>
        nativeRequestContext = <no data>
        moduleData = <no data>
        flags = <no data>

000000001e88e780 000007fef831f18a DomainNeutralILStubClass.IL_STUB(Int64, Int64, Int64, Int32)
    PARAMETERS:
        0x000000001cd7fc58
        <no data>
        <no data>
        <no data>

000000001e88efb0 000007fef1189a60 DomainNeutralILStubClass.IL_STUB(IntPtr, System.Web.RequestNotificationStatus ByRef)
    PARAMETERS:
        0x0000000000b51500

000000001e88f090 000007fef1189d8b System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr, IntPtr, IntPtr, Int32)
    PARAMETERS:
        managedHttpContext = <no data>
        nativeRequestContext = <no data>
        moduleData = <no data>
        flags = <no data>

000000001e88f210 000007fef1189294 System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr, IntPtr, IntPtr, Int32)
    PARAMETERS:
        managedHttpContext = <no data>
        nativeRequestContext = <no data>
        moduleData = <no data>
        flags = <no data>

000000001e88f270 000007fef831f3db DomainNeutralILStubClass.IL_STUB(Int64, Int64, Int64, Int32)
    PARAMETERS:
        0x000007fef830d14c
        <no data>
        <no data>
        <no data>

This has an object we can look at:

000000001e88dd60 000007fef2b4b031 System.Net.HttpWebRequest.SubmitRequest(System.Net.ServicePoint)
    PARAMETERS:
        this = 0x00000000033d3b48
        servicePoint = <no data>

so lets use the !do command.

0:018> !do 0x00000000033d3b48
Name: System.Net.HttpWebRequest
MethodTable: 000007fef2c22f48
EEClass: 000007fef29997d8
Size: 400(0x190) bytes
(C:\Windows\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fef37973f8  400018a        8        System.Object  0 instance 0000000000000000 __identity
000007fef31529b0  4001d76       28         System.Int32  1 instance                1 m_AuthenticationLevel
000007fef3f306a0  4001d77       2c         System.Int32  1 instance                4 m_ImpersonationLevel
000007fef2c22c70  4001d78       10 …equestCachePolicy  0 instance 00000000026b2930 m_CachePolicy
000007fef316c8a8  4001d79       18 …uestCacheProtocol  0 instance 0000000000000000 m_CacheProtocol
000007fef2c0bd50  4001d7a       20 …questCacheBinding  0 instance 00000000026b35d0 m_CacheBinding
000007fef379e9f0  4001d73      de0 …ections.ArrayList  0   shared           static s_PrefixList
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022bee98 000000001ab979c0:00000000026b00a0 <<
000007fef37973f8  4001d74      de8        System.Object  0   shared           static s_InternalSyncObject
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022bce20 000000001ab979c0:00000000026ae428 <<
000007fef3172938  4001d75      df0 …TimerThread+Queue  0   shared           static s_DefaultTimerQueue
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022bc428 000000001ab979c0:00000000026ae368 <<
000007fef316af60  4001d7b      df8 System.Net.IWebProxy  0   shared           static s_DefaultWebProxy
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022c3740 000000001ab979c0:00000000026b39b0 <<
000007fef3796cd8  4001d7c      990       System.Boolean  1   shared           static s_DefaultWebProxyInitialized
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:1 000000001ab979c0:1 <<
000007fef3796cd8  4001f37      164       System.Boolean  1 instance                0 m_Saw100Continue
000007fef3796cd8  4001f38      165       System.Boolean  1 instance                1 m_KeepAlive
000007fef3796cd8  4001f39      166       System.Boolean  1 instance                0 m_LockConnection
000007fef3796cd8  4001f3a      167       System.Boolean  1 instance                0 m_NtlmKeepAlive
000007fef3796cd8  4001f3b      168       System.Boolean  1 instance                0 m_PreAuthenticate
000007fef3144c48  4001f3c      130         System.Int32  1 instance                0 m_AutomaticDecompression
000007fef379ed78  4001f3d      134         System.Int32  1 instance                0 m_Aborted
000007fef3796cd8  4001f3e      169       System.Boolean  1 instance                0 m_OnceFailed
000007fef3796cd8  4001f3f      16a       System.Boolean  1 instance                1 m_Pipelined
000007fef3796cd8  4001f40      16b       System.Boolean  1 instance                0 m_Retry
000007fef3796cd8  4001f41      16c       System.Boolean  1 instance                0 m_HeadersCompleted
000007fef3796cd8  4001f42      16d       System.Boolean  1 instance                0 m_IsCurrentAuthenticationStateProxy
000007fef3796cd8  4001f43      16e       System.Boolean  1 instance                0 m_SawInitialResponse
000007fef3796cd8  4001f44      16f       System.Boolean  1 instance                0 m_BodyStarted
000007fef3796cd8  4001f45      170       System.Boolean  1 instance                1 m_RequestSubmitted
000007fef3796cd8  4001f46      171       System.Boolean  1 instance                0 m_OriginallyBuffered
000007fef3796cd8  4001f47      172       System.Boolean  1 instance                0 m_Extra401Retry
000007fef3144dd8  4001f4f      138        System.UInt32  1 instance                7 _Booleans
000007fef37d8040  4001f50      178      System.DateTime  1 instance 00000000033d3cc0 _CachedIfModifedSince
000007fef2c0db18  4001f51       30 …TimerThread+Timer  0 instance 0000000000000000 m_ContinueTimer
000007fef2c0f0c0  4001f52      180 …t.InterlockedGate  1 instance 00000000033d3cc8 m_ContinueGate
000007fef37973f8  4001f53       38        System.Object  0 instance 0000000002573160 m_PendingReturnResult
000007fef2c00018  4001f54       40 …t.LazyAsyncResult  0 instance 0000000000000000 _WriteAResult
000007fef2c00018  4001f55       48 …t.LazyAsyncResult  0 instance 00000000033d4500 _ReadAResult
000007fef2c00018  4001f56       50 …t.LazyAsyncResult  0 instance 00000000033d4778 _ConnectionAResult
000007fef2c00018  4001f57       58 …t.LazyAsyncResult  0 instance 00000000033d47c0 _ConnectionReaderAResult
000007fef3146c10  4001f58      13c         System.Int32  1 instance                0 _RequestIsAsync
000007fef316ea50  4001f59       60 …pContinueDelegate  0 instance 0000000000000000 _ContinueDelegate
000007fef2c0c9e8  4001f5a       68 ….Net.ServicePoint  0 instance 00000000026c34d0 _ServicePoint
000007fef2c23c08  4001f5b       70 …t.HttpWebResponse  0 instance 0000000000000000 _HttpResponse
000007fef37973f8  4001f5c       78        System.Object  0 instance 0000000002573160 _CoreResponse
000007fef379ed78  4001f5d      140         System.Int32  1 instance                0 _NestedWriteSideCheck
000007fef2c0c968  4001f5e       80 …Net.KnownHttpVerb  0 instance 00000000026b3b28 _Verb
000007fef2c0c968  4001f5f       88 …Net.KnownHttpVerb  0 instance 00000000026b3b28 _OriginVerb
000007fef2bffea8  4001f60       90 …bHeaderCollection  0 instance 00000000033d3e58 _HttpRequestHeaders
000007fef379fb48  4001f61       98        System.Byte[]  0 instance 0000000000000000 _WriteBuffer
000007fef3146718  4001f62      144         System.Int32  1 instance                4 _HttpWriteMode
000007fef2c28bd8  4001f63       a0           System.Uri  0 instance 00000000033d3a50 _Uri
000007fef2c28bd8  4001f64       a8           System.Uri  0 instance 00000000033d3a50 _OriginUri
000007fef3797b08  4001f65       b0        System.String  0 instance 0000000000000000 _MediaType
000007fef379b028  4001f66      128         System.Int64  1 instance -1 _ContentLength
000007fef316af60  4001f67       b8 System.Net.IWebProxy  0 instance 00000000026b39b0 _Proxy
000007fef3171a00  4001f68       c0 …em.Net.ProxyChain  0 instance 00000000033d45f0 _ProxyChain
000007fef3797b08  4001f69       c8        System.String  0 instance 0000000000000000 _ConnectionGroupName
000007fef3796cd8  4001f6a      173       System.Boolean  1 instance                0 m_InternalConnectionGroup
000007fef2c0caf8  4001f6b       d0 …thenticationState  0 instance 00000000033d4b68 _ProxyAuthenticationState
000007fef2c0caf8  4001f6c       d8 …thenticationState  0 instance 00000000033d4bb8 _ServerAuthenticationState
000007fef316ac68  4001f6d       e0 ….Net.ICredentials  0 instance 0000000000000000 _AuthInfo
000007fef2c0cbf8  4001f6e       e8 …HttpAbortDelegate  0 instance 00000000027f3c00 _AbortDelegate
000007fef2c0ea00  4001f6f       f0 …Net.ConnectStream  0 instance 0000000000000000 _SubmitWriteStream
000007fef2c0ea00  4001f70       f8 …Net.ConnectStream  0 instance 0000000000000000 _OldSubmitWriteStream
000007fef379ed78  4001f71      148         System.Int32  1 instance               50 _MaximumAllowedRedirections
000007fef379ed78  4001f72      14c         System.Int32  1 instance                0 _AutoRedirects
000007fef379ed78  4001f73      150         System.Int32  1 instance                0 _RerequestCount
000007fef379ed78  4001f74      154         System.Int32  1 instance           100000 _Timeout
000007fef2c0db18  4001f75      100 …TimerThread+Timer  0 instance 00000000033d4548 _Timer
000007fef3172938  4001f76      108 …TimerThread+Queue  0 instance 00000000026ae368 _TimerQueue
000007fef379ed78  4001f77      158         System.Int32  1 instance                0 _RequestContinueCount
000007fef379ed78  4001f78      15c         System.Int32  1 instance           300000 _ReadWriteTimeout
000007fef3169e88  4001f79      110 …t.CookieContainer  0 instance 0000000000000000 _CookieContainer
000007fef379ed78  4001f7a      160         System.Int32  1 instance               64 _MaximumResponseHeadersLength
000007fef2c0d8b0  4001f7b      118 …onnectionDelegate  0 instance 0000000000000000 _UnlockDelegate
000007fef2c04da0  4001f7c      120 …ificateCollection  0 instance 0000000000000000 _ClientCertificates
000007fef379fb48  4001f48      e80        System.Byte[]  0   shared           static HttpBytes
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022befe0 000000001ab979c0:00000000026b6928 <<
000007fef3786130  4001f49      e88 …ding.WaitCallback  0   shared           static s_EndWriteHeaders_Part2Callback
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022bf000 000000001ab979c0:00000000026b6948 <<
000007fef2c1d668  4001f4a      e90 …erThread+Callback  0   shared           static s_ContinueTimeoutCallback
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022bf040 000000001ab979c0:00000000026b6988 <<
000007fef3172938  4001f4b      e98 …TimerThread+Queue  0   shared           static s_ContinueTimerQueue
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022bf080 000000001ab979c0:00000000026b69c8 <<
000007fef2c1d668  4001f4c      ea0 …erThread+Callback  0   shared           static s_TimeoutCallback
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022bf158 000000001ab979c0:00000000026b6aa0 <<
000007fef3786130  4001f4d      ea8 …ding.WaitCallback  0   shared           static s_AbortWrapper
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022bf198 000000001ab979c0:00000000026b6ae0 <<
000007fef379ed78  4001f4e      9a8         System.Int32  1   shared           static s_UniqueGroupId
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0 000000001ab979c0:0 <<

now we getting somewhere, I see this as an HttpWebRequest Object, and in here I can access the URI:

000007fef2c28bd8  4001f63       a0           System.Uri  0 instance 00000000033d3a50 _Uri

ok, so lets dump this out

0:018> !DumpObj 00000000033d3a50
Name: System.Uri
MethodTable: 000007fef2c28bd8
EEClass: 000007fef29543b8
Size: 72(0x48) bytes
(C:\Windows\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fef3797b08  4001b8a        8        System.String  0 instance 00000000025ee6a0 m_String
000007fef3797b08  4001b8b       10        System.String  0 instance 0000000000000000 m_originalUnicodeString
000007fef2c28e60  4001b8c       18     System.UriParser  0 instance 000000000257e0c0 m_Syntax
000007fef3797b08  4001b8d       20        System.String  0 instance 0000000000000000 m_DnsSafeHost
000007fef313f598  4001b8e       30        System.UInt64  1 instance 37615763456 m_Flags
000007fef2c28fd0  4001b8f       28   System.Uri+UriInfo  0 instance 00000000033d3ac0 m_Info
000007fef3796cd8  4001b90       38       System.Boolean  1 instance                0 m_iriParsing
000007fef3797b08  4001b7f      c88        System.String  0   shared           static UriSchemeFile
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0000000002122830 000000001ab979c0:0000000002122830 <<
000007fef3797b08  4001b80      c90        System.String  0   shared           static UriSchemeFtp
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0000000002122810 000000001ab979c0:0000000002122810 <<
000007fef3797b08  4001b81      c98        System.String  0   shared           static UriSchemeGopher
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0000000002122858 000000001ab979c0:0000000002122858 <<
000007fef3797b08  4001b82      ca0        System.String  0   shared           static UriSchemeHttp
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000021227c0 000000001ab979c0:00000000021227c0 <<
000007fef3797b08  4001b83      ca8        System.String  0   shared           static UriSchemeHttps
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000021227e8 000000001ab979c0:00000000021227e8 <<
000007fef3797b08  4001b84      cb0        System.String  0   shared           static UriSchemeMailto
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000021228d0 000000001ab979c0:00000000021228d0 <<
000007fef3797b08  4001b85      cb8        System.String  0   shared           static UriSchemeNews
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000021228a8 000000001ab979c0:00000000021228a8 <<
000007fef3797b08  4001b86      cc0        System.String  0   shared           static UriSchemeNntp
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0000000002122880 000000001ab979c0:0000000002122880 <<
000007fef3797b08  4001b87      cc8        System.String  0   shared           static UriSchemeNetTcp
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0000000002122970 000000001ab979c0:0000000002122970 <<
000007fef3797b08  4001b88      cd0        System.String  0   shared           static UriSchemeNetPipe
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0000000002122998 000000001ab979c0:0000000002122998 <<
000007fef3797b08  4001b89      cd8        System.String  0   shared           static SchemeDelimiter
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000021227a0 000000001ab979c0:00000000021227a0 <<
000007fef31691a8  4001b91      ce0 …etSecurityManager  0   shared           static s_ManagerRef
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0000000000000000 000000001ab979c0:0000000000000000 <<
000007fef37973f8  4001b92      ce8        System.Object  0   shared           static s_IntranetLock
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0000000002123388 000000001ab979c0:000000000257e280 <<
000007fef3796cd8  4001b93      970       System.Boolean  1   shared           static s_ConfigInitialized
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:1 000000001ab979c0:0 <<
000007fef31403c8  4001b94      974         System.Int32  1   shared           static s_IdnScope
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0 000000001ab979c0:0 <<
000007fef3796cd8  4001b95      978       System.Boolean  1   shared           static s_IriParsing
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0 000000001ab979c0:0 <<
000007fef37973f8  4001b96      cf0        System.Object  0   shared           static s_initLock
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000022ec898 000000001ab979c0:0000000000000000 <<
000007fef3799400  4001b97      cf8        System.Char[]  0   shared           static HexUpperChars
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000021233a0 000000001ab979c0:000000000257e298 <<
000007fef3799400  4001b98      d00        System.Char[]  0   shared           static HexLowerChars
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:00000000021233d8 000000001ab979c0:000000000257e2d0 <<
000007fef3799400  4001b99      d08        System.Char[]  0   shared           static _WSchars
                                 >> Domain:Value  00000000017ea4f0:NotInit  000000001a9d6a70:0000000002123410 000000001ab979c0:000000000257e308 <<

excellent, now lets get the string value

0:018> !DumpObj 00000000025ee6a0
Name: System.String
MethodTable: 000007fef3797b08
EEClass: 000007fef339e550
Size: 126(0x7e) bytes
(C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: http://Romiko.com.au/test.jsp
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000007fef379ed78  4000096        8         System.Int32  1 instance               51 m_arrayLength
000007fef379ed78  4000097        c         System.Int32  1 instance               50 m_stringLength
000007fef3799550  4000098       10          System.Char  1 instance               68 m_firstChar
000007fef3797b08  4000099       20        System.String  0   shared           static Empty
                                 >> Domain:Value  00000000017ea4f0:0000000002101308 000000001a9d6a70:0000000002101308 000000001ab979c0:0000000002101308 <<
000007fef3799400  400009a       28        System.Char[]  0   shared           static WhitespaceChars
                                 >> Domain:Value  00000000017ea4f0:0000000002101af8 000000001a9d6a70:0000000002109b68 000000001ab979c0:0000000002572798 <<

YAY ! We can see from here that the policy search is calling the test.jsp page, also we notice this happening in the stack from within the Page_Init. Lets go and get this source code my disassembling the module Smile

We need to tell the customer the exact code causing this problem, the way to do that is to get the source code and look at the Page_Init for the policysearch class.

Lets go back to the stack and find out which dll this code is coming from

0:018> !clrstack
OS Thread Id: 0x121c (18)
Child-SP         RetAddr          Call Site
000000001e88dbc0 000007fef2b3e817 System.Threading.WaitHandle.WaitOne(Int64, Boolean)
000000001e88dc00 000007fef2b48641 System.Net.LazyAsyncResult.WaitForCompletion(Boolean)
000000001e88dc60 000007fef2b47f34 System.Net.Connection.SubmitRequest(System.Net.HttpWebRequest)
000000001e88dcf0 000007fef2b474f8 System.Net.ServicePoint.SubmitRequest(System.Net.HttpWebRequest, System.String)
000000001e88dd60 000007fef2b4b031 System.Net.HttpWebRequest.SubmitRequest(System.Net.ServicePoint)
000000001e88ddc0 000007ff00705bbb System.Net.HttpWebRequest.GetResponse()
000000001e88de50 000007fef09da30a RomikoCommon.PageBase.OnPreInit(System.EventArgs)

FOUND IT:

000000001e88de50 000007fef09da30a RomikoCommon.PageBase.OnPreInit(System.EventArgs)

what we need to do now is get this dll file, lets look at what is in the domain

0:018> !DumpDomain
————————————–
System Domain: 000007fef8933f20
LowFrequencyHeap: 000007fef8933f68
HighFrequencyHeap: 000007fef8933ff8
StubHeap: 000007fef8934088
Stage: OPEN
Name: None
————————————–
Shared Domain: 000007fef8934820
LowFrequencyHeap: 000007fef8934868
HighFrequencyHeap: 000007fef89348f8
StubHeap: 000007fef8934988
Stage: OPEN
Name: None
Assembly: 000000000172e0c0
Assembly: 000000001aa8f210
Assembly: 000000001ab529f0
Assembly: 000000001aa247e0
Assembly: 000000001aa9b220
Assembly: 000000001aac5970
Assembly: 000000000172e3c0
Assembly: 000000001aa8ef10
Assembly: 000000001aac5730
Assembly: 000000001ab52c30
Assembly: 000000001ca16590
Assembly: 00000000017e4f30
Assembly: 000000000172e540
Assembly: 000000001aa8f5d0
Assembly: 000000001ca03fd0
Assembly: 000000001aa8df50
Assembly: 00000000017e5230
Assembly: 000000001aa24ba0
Assembly: 00000000017e59b0
Assembly: 000000001aa25560
Assembly: 000000001ca16290
Assembly: 000000001aa253e0
Assembly: 000000001aa9a9e0
Assembly: 000000001aa8e0d0
Assembly: 000000001aa8f450
————————————–
Domain 1: 00000000017ea4f0
LowFrequencyHeap: 00000000017ea538
HighFrequencyHeap: 00000000017ea5c8
StubHeap: 00000000017ea658
Stage: OPEN
SecurityDescriptor: 00000000017c9d60
Name: DefaultDomain
Assembly: 000000000172e0c0 [C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 000000000172e180
SecurityDescriptor: 000000000172df40
  Module Name
000007fef3361000 C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll
000007ff000e2568 C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\sortkey.nlp
000007ff000e2020 C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\sorttbls.nlp

I cut allot out, but lets look at this, we have our romikocommon.dll further down

Assembly: 00000000017e6c70 [C:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\purchasepath\2d20f8c3\9af2cafc\assembly\dl3\455d0c2c\ff027527_32ffcb01\RomikoCommon.DLL]
ClassLoader: 00000000017e6d30
SecurityDescriptor: 00000000017e6bb0
  Module Name
000007ff00176880 C:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\purchasepath\2d20f8c3\9af2cafc\assembly\dl3\455d0c2c\ff027527_32ffcb01\CommonCommon.DLL

Nice, now, we going to dump this to a file Smile

We will use the savemodule command, but we need the base address, so issue the LM command

0:018> LM
start             end                 module name
00000000`1c170000 00000000`1c197000   System_EnterpriseServices_Wrapper_1c170000   (no symbols)          
00000000`1c290000 00000000`1c2b6000   ICSharpCode_SharpZipLib   (no symbols)          
00000000`1cdf0000 00000000`1d0ef000   System_Data   (export symbols)       System.Data.dll
00000000`6b570000 00000000`6bb04000   AjaxControlToolkit   (no symbols)                   
00000000`72a00000 00000000`72a18000   RomikoCommon_72a00000 C (no symbols)                 
00000000`72ee0000 00000000`72ef8000   App_Web_jweapfn4 C (no symbols)          
00000000`72f00000 00000000`72f0a000   App_Web_xg2ybqom C (no symbols)          
00000000`72f10000 00000000`72f18000   App_Web_1elshlii C (no symbols)          
00000000`72f20000 00000000`72f38000   RomikoCommon C (no symbols) 

perfect now we export the dll for .net reflector to use or ildasm, grabbing the base address above and dumping the contents as a dll.

0:018> !SaveModule 00000000`72a00000  e:\projects\debug\romikocommom.dll

remember in the stack we had this

RomikoCommon.PageBase.OnPreInit(System.EventArgs)

The savemodule has saved/dumped the dll, in reflector we can see the code causing the problem!

image

Here is the code

 protected override void OnPreInit(EventArgs e)
    {
        string str = (this._deployOrDev.ToUpper() == "DEV") ? this._sessionId : base.Request.QueryString["sid"];
        if (!string.IsNullOrEmpty(str))
        {
            string str2 = this.Session["sid"] as string;
            if (!string.IsNullOrEmpty(str2) && (string.Compare(str2, str, true) != 0))
            {
                this.Session.Clear();
            }
            this.Session["sid"] = str;
        }
        else
        {
            str = this.Session["sid"] as string;
        }
        if (string.IsNullOrEmpty(str))
        {
            throw new SecurityException("Session is empty.");
        }
        try
        {
            using (SecurityServicesProxy proxy = new SecurityServicesProxy())
            {
                UserCredential = proxy.GetUserCredential(str, Encrypt.EncryptData(str));
                if (UserCredential == null)
                {
                    throw new SessionExpiredException("The session is invalid. Please login again.");
                }
            }
        }
        catch (Exception exception)
        {
            HandleException(base.GetType().Name, MethodBase.GetCurrentMethod().Name, "Session Indentifier:" + str, exception, new Action<string>(this.DisplayError));
        }
        this.SetUserCredentialsForSession();
        try
        {
            WebRequest request = WebRequest.Create(this._keepAliveUrl);
            request.Headers.Add(HttpRequestHeader.Cookie, "JSESSIONID=" + str);
            request.GetResponse();
        }
        catch (Exception exception2)
        {
            _log.Info(exception2);
            throw new SessionExpiredException("Connecting Java system failed.", exception2);
        }
    }

WOW, look, every PAGE in their system is inheriting a custom pagebase, and this is called a external web page (remember test.jsp. It is doing this to keep the java session alive since the .net session is in an Iframe.

My load test always showed errors Connecting to Java system failed and here we can see why. The code should be changed, instead of all page loads calling java, rather have a background thread run and do it every 30 seconds whatever and we improve the performance of EVERY page.

we can also check that the request was not working and was waiting for the javasystem which crashed by checking cpu time.
0:018> kerneltime

Couldn’t resolve error at ‘erneltime’

0:018> .time

Debug session time: Fri May  6 15:31:14.000 2011 (UTC + 10:00)

System Uptime: 58 days 6:25:30.968

Process Uptime: 0 days 0:28:42.000

  Kernel time: 0 days 0:00:01.000

  User time: 0 days 0:00:05.000

it only using a fraction of a second, so it was definitely handing and waiting for a response, which of course would timeout at some point.

I hope you enjoyed this debugging session as much as I did, and thank you Tatham Oddie for geeking it up with me on this Smile

Romiko Derbynew

NHibernate/NHProf Dependency Injection and Sessions

Hi,

I recently been working with the Castle.Components.Scheduler and Jobs. The job runs in a NServiceBus host on Version:  2.0.0.1219

Basically, I created a Job that implemented IJob and had constructor injection for the NHibernate Session.

<!–Castle.Components.Scheduler–>
<component id=”TheJob”
service=”Castle.Components.Scheduler.IJob”
type=”Romiko.TheJob, Romiko”
lifestyle=”transient”/>

  public class TheJob : TheJobBase, IJob
    {
        public TheJob(Idependency1 dep1, Idependency2 dep2, ISession myNHSession)
            : base(dep1, dep2,myNHSession) )
        {
        }

        public override bool Execute(JobExecutionContext context)
        {
            Process(context);
            return true;
        }

I colleague of mine, Matthew Wills, from Readify has been telling me how much NHProf can save you time, and indeed today it was proved Smile

The process method above runs a series of job steps which execute 3 SQL select staments.

NHProf was moaning about the Session being shared:

Alert: Using a single session in multiple threads is likely a bug

Alert: Use of implicit transactions is discouraged

 

We noticed that subsequent job runs were using the same session!

image

See above, that 1 session is being used by the job, which runs all the time, this is not good at all for performance. The above shows the job running 4 to 5 times, each time executing 3 statements.

So, what we did, was remove the Session from the constructor injection, as we found a bug where a session was being created on one thread (in constructor) and then used in another thread (in the execute method).

To solve the problem above, we used Windsor to resolve the dependency in the execute method instead.

 public override bool Execute(JobExecutionContext context)
        {
            _session = WindsorAccessor.Container.Resolve<ISession>();
            Process(context);
            return true;
        }

Now, when we run NHProf, we get a sessoin per job and no longer hold a session for along time on a long running process within NServiceBus.

Lets rerun the code now and see how it works when we run 4 to 5 jobs Smile

image

 

From the above, we can now see different sessions per JOB CYCLE. This will consume less resources and we solved the red nasty warnings in NHProf.

Mathew pointed out that we can go further, and optimised the 3 transactions above to be called in BATCH. So we can optimise the QueryOver methods to use Future operations, which will then batch the 3 transactions, which I can then use from within the job process, thus reducing each job cycle from 3 database calls to 1.

What i did was use something along the line of Future in my code and then delay looping through my result set until the last minute, so some code shuffling around Smile

 return Session.QueryOver<RomikoTable1>()
                .Where(x => x.CreatedDate >= RegDate
                    && x.CreatedDate <= dateUnreg
                    && x.Count == pollCount
                    && x.BobTheBuilder == null)
                .JoinQueryOver(y => y.TableTwo)
                .JoinQueryOver(z => z.PostManPat).Where(a => a.Id == PostManPatId)
                .Future<RomikoTable1>();

 

Now check the BATCHING, hence less connections, notice duration is for all of them, where before you see a duration for each query.

image

 

I would like to thank Matthew Wills for show me some new cool tools that will definitely help me along the road in the future!

I would say that NHProf is definitely worth buying!

plupload property items will be lost if you use Resize in Script Code.

Hi,

There is a small bug in PLUpload, a tool I highly recommend to use to upload files to MVC 3. It works really well.

The place to download the tool is from:

http://www.plupload.com/

Now, ensure this is disabled or commented out, else you will LOSE all image properties, such as camera settings and GPS. So ensure        // resize: { width: 320, height: 240, quality: 90 } is not used!

<script type="text/javascript">
    // Convert divs to queue widgets when the DOM is ready
    $(function () {
        $("#uploader").plupload({
            // General settings
            runtimes: ‘flash,html5,browserplus,silverlight,gears,html4’,
            url: ‘home/upload’,
            max_file_size: ‘1000mb’,
            max_file_count: 20, // user can add no more then 20 files at a time
            chunk_size: ‘1mb’,
            unique_names: true,
            multiple_queues: true,

            // Resize images on clientside if we can
           // resize: { width: 320, height: 240, quality: 90 },

            // Rename files by clicking on their titles
            rename: true,

            // Sort files
            sortable: true,

            // Specify what files to browse for
            filters: [
            { title: "Image files", extensions: "jpg,gif,png" },
            { title: "Zip files", extensions: "zip,avi" }
        ],

            // Flash settings
            flash_swf_url: ‘/Scripts/plupload/plupload.flash.swf’,

            // Silverlight settings
            silverlight_xap_url: ‘/Scripts/plupload/plupload.silverlight.xap’
        });

        // Client side form validation
        $(‘form’).submit(function (e) {
            var uploader = $(‘#uploader’).plupload(‘getUploader’);

            // Validate number of uploaded files
            if (uploader.total.uploaded == 0) {
                // Files in queue upload them first
                if (uploader.files.length > 0) {
                    // When all files are uploaded submit form
                    uploader.bind(‘UploadProgress’, function () {
                        if (uploader.total.uploaded == uploader.files.length)
                            $(‘form’).submit();
                    });

                    uploader.start();
                } else
                    alert(‘You must at least upload one file.’);

                e.preventDefault();
            }
        });

    });
</script>

 

If it is not commented out, the property list of the image will only have two items.

image

UPDATE 24/02/2011: It does keep properties with Image Resize if HTML 5 is being used, FLASH and SILVERLIGHT does not work currently with it, so if in doubt, use HTML 5 first.

Azure Blob Storage Helper Class and Shared Access Policy

Hi,

I have create a simple helper class that can be used to Upload Blobs to a public and private container. It then also allows you to grant users Temporary access at the blob level for 2 days. This is nice when you want to provide a download link that will expire.

Below is the class

using System.IO;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;



namespace Common.Azure
{
    public interface IStorage
    {

        string ContainerThumbnails { get; }
        string ContainerPhotos { get; }

        CloudStorageAccount StorageAccountInfo { get; set; }
        CloudBlobClient BlobClient { get; set; }

        CloudBlob UploadBlob(string blobUri, Stream stream, string containerName, bool isPublic);
        string GetSharedAccessSignatureToDownloadBlob(string blobUri, string containerName, string userName);
    }
}

 

using System;
using System.Configuration;
using System.IO;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;

namespace Common.Azure
{
    public class Storage : IStorage
    {
        public string ContainerThumbnails
        {
            get { return "photothumbnails"; }
        }
        public string ContainerPhotos
        {
            get { return "photos"; }
        }


        public CloudStorageAccount StorageAccountInfo { get; set; }
        public CloudBlobClient BlobClient { get; set; }

        public Storage()
        {

            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                if (RoleEnvironment.IsAvailable)
                    configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
                else
                    configSetter(ConfigurationManager.AppSettings[configName]);
            });

            StorageAccountInfo = CloudStorageAccount.FromConfigurationSetting("StorageConnectionString");
            BlobClient = StorageAccountInfo.CreateCloudBlobClient();
        }



        public CloudBlob UploadBlob(string blobUri, Stream stream, string containerName, bool isPublic)
        {
            var container = BlobClient.GetContainerReference(containerName);
            container.CreateIfNotExist();

            if (isPublic)
            {
                var permissions = new BlobContainerPermissions
                {
                    PublicAccess = BlobContainerPublicAccessType.Container
                };
                container.SetPermissions(permissions);
            }
            else
            {
                var permissions = new BlobContainerPermissions
                {
                    PublicAccess = BlobContainerPublicAccessType.Off
                };
                container.SetPermissions(permissions);
            }


            var blob = container.GetBlockBlobReference(blobUri);
            blob.UploadFromStream(stream);
            return blob;
        }

        public string GetSharedAccessSignatureToDownloadBlob(string blobUri, string containerName, string userName)
        {
            var container = BlobClient.GetContainerReference(containerName);
            container.CreateIfNotExist();
            var blob = container.GetBlockBlobReference(blobUri);




              
            var containeraccess= new SharedAccessPolicy();
            containeraccess.Permissions = SharedAccessPermissions.Read;

            var blobaccess = new SharedAccessPolicy
                                 {
                                     SharedAccessExpiryTime = DateTime.UtcNow.AddDays(2)
                                 };

            var perm = new BlobContainerPermissions
                           {
                               PublicAccess = BlobContainerPublicAccessType.Off
                           };
            perm.SharedAccessPolicies.Clear();
            perm.SharedAccessPolicies.Add(userName, containeraccess);

            container.SetPermissions(perm, new BlobRequestOptions());

            return blob.GetSharedAccessSignature(blobaccess, userName);
        }
    }
}

Now, in a MVC 3 controller, I can call the helper class when an order is submitted and removed from the shopping cart:

orderDetails.DownloadLink = storage.GetSharedAccessSignatureToDownloadBlob(photo.Photo_Url),
                                                                          "photos",
                                                                          cartItem.Username);

 

That’s all this is to it, then on the downloads link, you just render the shared access url

   //
        //Get: /Download/
        [HttpGet]
        public ActionResult Download(int photoId)
        {
            var userName = HttpContext.User.Identity.Name;
            OrderDetail order = _storeDb.OrderDetails.Include("Photo").Include("Order").First(x => x.PhotoId == photoId && x.Order.Username == userName);

            if (order == null)
                return View("InvalidPhoto");

            order.DownloadCount += 1;
            _storeDb.SaveChanges();

            string path = order.Photo.Photo_Url + order.DownloadLink;
            return base.Redirect(path);

        }

e.g.

http://myblobs.com/containername/blobname.jpg?se=2011-02-22T01%3A07%3A20Z&sr=b&si=romiko&sig=PsfUXcJtWRoWBvIiz%2FvHoUJnYF2D70%2B3CdlBbn9SiOM%3D

Using Autofac, MVC3, SQLMembershipProvider and Entity Framework in Windows Azure and SQL Azure

We will cover some configuration issues when deploying MVC3/Razor  Web Apps to the cloud

MVC Configuration

To get MVC 3/Razor working with Azure, follow these instructions:

MVC3 Assembly References

Once you have a SQL Azure instance running, you will need to connect to it from the SQL Management Studio, ensure you set the default database (it is on master, so ensure you change it to your database name, else it will not connect).

Configuration e.g. Database Connection Strings, SMTP settings etc

When we are developing on a local machine without the development fabric, e.g. F5 from the web project and not from the cloud project, the configuration information will be coming from the web.config or app.config (worker role).

So we need to ensure that ALL cloud configuration information for connection strings and other configuration values are stored at the PACKAGE level i.e. ServiceConfiguration.cscfg

However, when you add key/value pairs to the ServiceConfiguration.cscfg you will need to define them in the ServiceConfiguration.csdef file as well, but without the values.

So lets get this setup

——————————————————————Web,config———————————————————————–

  <connectionStrings>
    <add name="ApplicationServices" connectionString="Data Source=.;Initial Catalog=Readify.Romiko;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
    <add name="RomikoEntities" connectionString="metadata=res://*/Repository.Romiko.csdl|res://*/Repository.Romiko.ssdl|res://*/Repository.Romiko.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.;Initial Catalog=Readify.Romiko;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>

——————————————————————————————————————————————————–

As we can see above, we got the EF connection string and a SQLMembershipProvider connection string.

Excellent, now lets define these key/value pairs in the ServiceConfiguration.csdef

Now, I use these connections from both a worker and web role, so you need to define it in the respective sections:

——————————————————————ServiceConfiguration.csdef ————————————————-

<ServiceDefinition name="Readify.Romiko.Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WebRole name="Readify.Romiko.Web">
    <Sites>
      <Site name="Web">
        <Bindings>
          <Binding name="Endpoint1" endpointName="Endpoint1" />
        </Bindings>
      </Site>
    </Sites>
    <Endpoints>
      <InputEndpoint name="Endpoint1" protocol="http" port="80" />
    </Endpoints>
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
    <ConfigurationSettings>
      <Setting name="RomikoEntities" />
      <Setting name="ApplicationServices" />
    </ConfigurationSettings>
  </WebRole>
  <WorkerRole name="Readify.Romiko.Worker">
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
    <ConfigurationSettings>
      <Setting name="RomikoEntities" />
      <Setting name="SMTPServer" />
      <Setting name="SMTPServerPort" />
      <Setting name="SMTPUser" />
      <Setting name="SMTPPassword" />
    </ConfigurationSettings>
  </WorkerRole>
</ServiceDefinition>

———————————————————————————————————————————————————

As you can see above, I only need the EF connection string in the worker role, as I do not use forms authentication there! Also notice, there is no values set, this will be done in the csfg file:

——————————————————————ServiceConfiguration.cscfg —————————————————–

<Role name="Readify.Romiko.Web">
   <Instances count="1" />
   <ConfigurationSettings>
     <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="UseDevelopmentStorage=true" />
     <Setting name="RomikoEntities" value="metadata=res://*/Repository.Romiko.csdl|res://*/Repository.Romiko.ssdl|res://*/Repository.Romiko.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=ifx2adecne.database.windows.net;Initial Catalog=Romiko;Persist Security Info=True;User ID=RomikoApp;Password=St0rmyCloud@pp1&quot;" />
     <Setting name="ApplicationServices" value="Data Source=ifx2adecne.database.windows.net;Initial Catalog=Romiko;Persist Security Info=True;User ID=RomikoApp;Password=St0rmyCloud@pp1" />
   </ConfigurationSettings>
 </Role>
 <Role name="Readify.Romiko.Worker">
   <Instances count="1" />
   <ConfigurationSettings>
     <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="UseDevelopmentStorage=true" />
     <Setting name="RomikoEntities" value="metadata=res://*/Repository.Romiko.csdl|res://*/Repository.Romiko.ssdl|res://*/Repository.Romiko.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=ifx2adecne.database.windows.net;Initial Catalog=Romiko;Persist Security Info=True;User ID=RomikoApp;Password=St0rmyCloud@pp1&quot;" />
     <Setting name="SMTPServer" value="smtp.mail.Romiko.com" />
     <Setting name="SMTPServerPort" value="25" />
     <Setting name="SMTPUser" value="Romiko@romiko.net" />
     <Setting name="SMTPPassword" value="derbynew" />
   </ConfigurationSettings>
 </Role>

———————————————————————————————————————————————————

Configuration Classes

Excellent, now we need a way to tell the runtime when to load data from the Azure configuration or the standard .NET configuration files.

All we do is create a helper class and interface, the interface will be used for IoC injection later with Autofac.

image

 

Now, the two concrete classes just fetch config info from seperate managers:

 public class ConfigurationManagerAzure : IConfigurationManager
    {
        public string GetDatabaseConnectionString()
        {

            return RoleEnvironment.GetConfigurationSettingValue("RomikoEntities");

        }

        public string AuthenticationProviderConnectionString()
        {

            return RoleEnvironment.GetConfigurationSettingValue("ApplicationServices");

        }


        public SmtpSettings GetSmtpSettings()
        {
            var smtpSettings = new SmtpSettings();

            smtpSettings.SmtpServer = RoleEnvironment.GetConfigurationSettingValue("SMTPServer");
            smtpSettings.SmtpServerPort = RoleEnvironment.GetConfigurationSettingValue("SMTPServerPort");
            smtpSettings.SmtpUser = RoleEnvironment.GetConfigurationSettingValue("SMTPUser");
            smtpSettings.SmtpPassword = RoleEnvironment.GetConfigurationSettingValue("SMTPPassword");

            return smtpSettings;
        }
    }

 

 public class ConfigurationManagerLocal : IConfigurationManager
    {
        public string GetDatabaseConnectionString()
        {

            return System.Configuration.ConfigurationManager.ConnectionStrings["RomikoEntities"].ToString();

        }

        public string AuthenticationProviderConnectionString()
        {

            return System.Configuration.ConfigurationManager.ConnectionStrings["ApplicationServices"].ToString();

        }


        public SmtpSettings GetSmtpSettings()
        {
            var smtpSettings = new SmtpSettings();

            smtpSettings.SmtpServer = System.Configuration.ConfigurationManager.AppSettings["SMTPServer"];
            smtpSettings.SmtpServerPort = System.Configuration.ConfigurationManager.AppSettings["SMTPServerPort"];
            smtpSettings.SmtpUser = System.Configuration.ConfigurationManager.AppSettings["SMTPUser"];
            smtpSettings.SmtpPassword = System.Configuration.ConfigurationManager.AppSettings["SMTPPassword"];


            return smtpSettings;
        }
    }

This is perfect, now what we can do is load the concrete class dynamically via AutoFac, so in the Global.asax.cs file we have something like this:

AutoFac (Web Role)

protected void Application_Start()
       {

           RegisterGlobalFilters(GlobalFilters.Filters);
           RegisterRoutes(RouteTable.Routes);


           var builder = new ContainerBuilder();
           builder.RegisterControllers(Assembly.GetExecutingAssembly());


           if (RoleEnvironment.IsAvailable)
               builder.RegisterType<ConfigurationManagerAzure>().AsImplementedInterfaces();
           else
               builder.RegisterType<ConfigurationManagerLocal>().AsImplementedInterfaces();

           builder.RegisterType<RomikoEntities>();
           builder.RegisterType<AccountMembershipService>().AsImplementedInterfaces();
           builder.RegisterType<Membership.AzureMembershipProvider>();
           builder.RegisterType<FormsAuthenticationService>().AsImplementedInterfaces();

           builder.Register(x => System.Web.Security.Membership.Provider).ExternallyOwned();

           Container = builder.Build();
           DependencyResolver.SetResolver(new AutofacDependencyResolver(Container));

       }

So, from the above we used the RoleEnvironment.isAvailable to detect if it is running on the cloud,, and then load the correct class types into the container.

Just ensure you controllers and helper classes have constructor’s for injecting the IConfiguration and Entities e.g.

Controller:

public HomeController(RomikoEntities entities)
        {
            this.entities = entities;
        }

Helper Class injection setup:

public class AlertManager
   {
       private readonly IConfigurationManager configurationManager;
       private readonly RomikoEntities entities;

       public PolicyAlertManager(
           IConfigurationManager configurationManager,
           RomikoEntities entities)
       {
           this.configurationManager = configurationManager;
           this.entities = entities;
       }

 

EF partial class extended

To get EF to use the connection string from the inject configuration concrete class we have this partial class:

public partial class RomikoEntities
    {
        public RomikoEntities(IConfigurationManager configurationManager)
            : this(configurationManager.GetDatabaseConnectionString())
        {

        }
    }

As we get can, EF has a default constructor that we can call to pass in the connection string.

AccountController and SQLMembershipProvider

Now, we need to make some modifications to the default account controller that comes with an MVC project, if you choose the template.

First of all, we will ensure Autofac registers a method call:

   builder.Register(x => System.Web.Security.Membership.Provider).ExternallyOwned();

This is used in the accountmodel class:

I modified the AccountController.cs to have the following constructor injection possible:

private  IFormsAuthenticationService formsService;
private  IMembershipService membershipService;

public AccountController(IMembershipService membershipService, IFormsAuthenticationService formsService)
{
    this.MembershipService = membershipService;
    this.FormsService = formsService;
}

public IMembershipService MembershipService
{
    get { return membershipService; }
    set { membershipService = value; }
}

public IFormsAuthenticationService FormsService
{
    get { return formsService; }
    set { formsService = value; }
}

 

Then we need to modify the AccountModel class to get the correct constructor parameters, since this is configured via the web.,config and it expects a paramterless constructor:

<membership>
  <providers>
    <clear />
    <add name="AspNetSqlMembershipProvider" type="Readify.Romiko.Web.Membership.AzureMembershipProvider" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
  </providers>
</membership>

So, we have a custom MemberShipProvider that inherits from SqlMembershipProvider:

AzureMembershipProvider

using System.Collections.Specialized;
using System.Reflection;
using System.Web.Security;
using Autofac;
using Readify.Romiko.Common.Configuration;

namespace Readify.Romiko.Web.Membership
{
    public class AzureMembershipProvider : SqlMembershipProvider
    {
        private readonly IConfigurationManager configuration;

        public AzureMembershipProvider()
            :this((MvcApplication.Container.Resolve<IConfigurationManager>()))
        {
        }

        public AzureMembershipProvider(IConfigurationManager configuration)
            
        {
            this.configuration = configuration;
        }

        public override void Initialize(string name, NameValueCollection config)
        {
            base.Initialize(name, config);

            var connectionString = configuration.AuthenticationProviderConnectionString();
            var connectionStringField = typeof(SqlMembershipProvider).GetField("_sqlConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
            if (connectionStringField != null) connectionStringField.SetValue(this, connectionString);
        }
    }
}

 

Notice, above, we have kept the constructor parameter less, but then we use AutoFac to resolve the configuration:

Note MVCApplication is the class name of the Global.asax.cs file! Also we have a static reference to the Container.

Excellent, so now we can inject configuration, however, the AccountModel.cs file needs some pimping as well, remember this resolver:

builder.Register(x => System.Web.Security.Membership.Provider).ExternallyOwned();

Well, we need to implement this in the Account.Model, so it is available, again, we will resolve it like so:

In the AccountModel.cs there is a class called: AccountMembershipService, we will modify the constructor to do some resolving:

public class AccountMembershipService : IMembershipService
    {
        private readonly MembershipProvider _provider;

        public AccountMembershipService()
            : this(MvcApplication.Container.Resolve<MembershipProvider>())
        {
        }

        public AccountMembershipService(MembershipProvider provider)
        {
            _provider = provider ?? System.Web.Security.Membership.Provider;
        }
……..

So, as you can see we just use a parameter less constructor pattern and then within that constructor we resolve types and method/property calls.

So when Membership.Provider is called, the container will get the return value.

Autofac install with Nuget

In VS2010, we can go to View->Other Windows-> Package manager console, and install autofac into the various projects:

Note the default project, ensure you install to the respective projects:

image

Command to install is:

PM> install-package Autofac.Mvc3

You can read more about this here:

http://code.google.com/p/autofac/wiki/Mvc3Integration

SQL Azure Membership Role Database Setup

This is interesting, as you will need custom scripts that are azure friendly, e.g. SQL Azure does not use USE statements! Makes sense from a security perspective.

Updated ASP.net scripts for use with Microsoft SQL Azure

http://support.microsoft.com/kb/2006191/en-us

 

AutoFac and Worker Roles:

This is easy to setup, just put the logic in the OnStart Method of the WorkerRole.cs file.

  public override bool OnStart()
        {
            ServicePointManager.DefaultConnectionLimit = 12;

            //AutoFac Container
            var builder = new ContainerBuilder();

            if (RoleEnvironment.IsAvailable)
                builder.RegisterType<ConfigurationManagerAzure>().AsImplementedInterfaces();
            else
                builder.RegisterType<ConfigurationManagerLocal>().AsImplementedInterfaces();

            builder.RegisterType<AlertManager>();
            builder.RegisterType<RomikoCloudEntities>();

            container = builder.Build();

            return base.OnStart();
        }

As you can see the Alertmanager constructor has all the interfaces defined Smile

 public class AlertManager
    {
        private readonly IConfigurationManager configurationManager;
        private readonly RomikoCloudEntities entities;

        public PolicyAlertManager(
            IConfigurationManager configurationManager,
            RomikoCloudEntities entities)
        {
            this.configurationManager = configurationManager;
            this.entities = entities;
        }

So, I hope this gets you going with MVC3, Azure and all the bells and whistles that it comes with.