Saturday, June 16, 2012

Injection Logger in Orchard


Introduction:

Orchard using Aufofac as DI container. The blog just try to explain one of those usage: Inject Logging. In any system logging is a very basis function and normally when we need to log something, we either create a new Logger instance or get a global instance to do the logging. while, Orchard using a different way to get the Logger instance. Whenever a class requires a Logger instance, all it needs to do is to declare a ILogger property, that’s it. And later, in your class, you can use this property to Logging at anytime. So, how it happening? Who and when this property been set?

Register Logger:

When Orchard web application startup, the OrchardStarter will be used to do most of the registration work.
public static IContainer CreateHostContainer(Action<ContainerBuilder> registrations) {
      var builder = new
ContainerBuilder();
      builder.RegisterModule(new
CollectionOrderModule());
      builder.RegisterModule(new
LoggingModule());

}
The LoggingModule is registered to the Autofac ContainerBuilder. Let’s look at the detail of LoggingModule
public class LoggingModule : Module {
}
It inherites from the Autofac.Module. Autofac.Module provide the method that you can register a group of types in one place.But we only want to register the Logger, why use the Module? The Module provide extra methods that can hooked to the whole registration event so that we can has the chance to inject Logger intance to other object’s property. This method used in LoggingModule is  
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) {
           var implementationType = registration.Activator.LimitType;
           // build an array of actions on this type to assign loggers to member properties
           var injectors = BuildLoggerInjectors(implementationType).ToArray();
            // if there are no logger properties, there's no reason to hook the activated event
           if (!injectors.Any())
               return;
           // otherwise, whan an instance of this component is activated, inject the loggers on the instance
           registration.Activated += (s, e) => {
               foreach (var injector in injectors)
                   injector(e.Context, e.Instance);
           };

}
AttachToComponentRegistration will be used by the Autofac to attach to both the existing registered components and the  future components that will be registered to this ContainerBuilder. In the code with red color, we can see when a component will be activated(or resolved), we will run each injector to this new created instance. What is the injector? Let’s see the detail of BuillLoggerInjectors
private IEnumerable<Action<IComponentContext, object>> BuildLoggerInjectors(Type componentType) {
           // Look for settable properties of type "ILogger"
           var loggerProperties = componentType
               .GetProperties(
BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance)
               .Select(p => new {
                   PropertyInfo = p,
                   p.PropertyType,
                   IndexParameters = p.GetIndexParameters(),
                   Accessors = p.GetAccessors(false)
               })
               .Where(x => x.PropertyType == typeof(
ILogger)) // must be a logger
               .Where(x => x.IndexParameters.Count() == 0) // must not be an indexer
               .Where(x => x.Accessors.Length != 1 || x.Accessors[0].ReturnType == typeof(void)); //must have get/set, or only set

           // Return an array of actions that resolve a logger and assign the property
           foreach (var entry in loggerProperties) {
               var propertyInfo = entry.PropertyInfo;

               yield return (ctx, instance) => {
                   string component = componentType.ToString();
                   var logger = _loggerCache.GetOrAdd(component, key => ctx.Resolve<
ILogger>(new TypedParameter(typeof(Type), componentType)));
                   propertyInfo.SetValue(instance, logger, null);
               };
           }
       }

In this method, it first find all the properties that type is ILogger and is not indexer and it can  be set. Next for each of these properties, it will create a Action that set the property to ILogger instance that from local cache or resolve from the context. In the Load method of LoggingModule, it register the LoggerFactory and the ILogger
protected override void Load(ContainerBuilder moduleBuilder) {
           // by default, use Orchard's logger that delegates to Castle's logger factory
           moduleBuilder.RegisterType<
CastleLoggerFactory>().As<ILoggerFactory>().InstancePerLifetimeScope();
moduleBuilder.RegisterType<
OrchardLog4netFactory>().As<Castle.Core.Logging.ILoggerFactory>().InstancePerLifetimeScope();
           // call CreateLogger in response to the request for an ILogger implementation
           moduleBuilder.Register(CreateLogger).As<
ILogger>().InstancePerDependency();
}

We can see register ILogger using a delegate. Why? Because we don’t want to create Logger for each activated instance, for the same type of instance, we just want to use the same Logger instance.
private static ILogger CreateLogger(IComponentContext context, IEnumerable<Parameter> parameters) {
           // return an ILogger in response to Resolve<ILogger>(componentTypeParameter)
           var loggerFactory = context.Resolve<
ILoggerFactory>();
           var containingType = parameters.TypedAs<
Type>();
           return loggerFactory.CreateLogger(containingType);
}

We can see the loggerFactory.CreateLogger passed in the type of the current activated type.

Monday, January 16, 2012

Open Access 2003 under Access 2007 with mdw file

When you open the Access 2003 file using Access 2007, it will using the default mdw file and you lost all of your use security. So what you need to do is create a shortcut and point it to your mdb file and the mdw file like "path to mdb file" /wrkgrp "path to secure.mdw"

After that, you can verify the mdw file is correct loaded by press Ctrl-G and enter the ?SysCmd(acSysCmdGetWorkgroupFile) into to vb immediate windows.

Sunday, July 3, 2011

Enable Https for WCF

To enable Https for WCF, you need to change the config in the service site. For host in IIS, you need to make sure do not assign the name to bindings/[binding type]/binding and do not assign binding name in services/service/endpoint. Below is the sample:

<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="HttpsBehavior" >
<serviceMetadata httpsGetEnabled="true" httpGetEnabled="true" policyVersion="Policy12"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="TestService" behaviorConfiguration="HttpsBehavior" >
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
<endpoint
address=""
binding="basicHttpBinding" "No Bindding Name here"
contract="mProductive.PortalAdmin.WebSite.IWebPurchase"
/>
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" >
<baseAddressPrefixFilters>
<add prefix="http://localhost/"/>
</baseAddressPrefixFilters>
</serviceHostingEnvironment>
<bindings >
<basicHttpBinding>
<binding "No Bindding Name here">
<security mode="Transport">
<transport clientCredentialType="None"/></security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>

Tuesday, November 3, 2009

supportsMaintainScrollPositionOnPostback On Chrome

Add a new file called others.browser to the app_browser folder. This can also fix the asp.net Menu control under chrome. The file content as below:
<browsers>
<browser id="Safari3" parentID="Safari1Plus">
<identification>
<userAgent match="Safari/\d+\.\d+" />
</identification>
<capture>
<userAgent match="Version/(?'version'\d+\.\d+)" />
</capture>
<capabilities>
<capability name="browser" value="Safari3" />
<capability name="version" value="${version}" />
</capabilities>
<controlAdapters>
<adapter controlType="System.Web.UI.WebControls.Menu"
adapterType="" />
</controlAdapters>
</browser>
<browser id="GoogleChrome" parentID="Safari3">
<identification>
<userAgent match="Chrome/(?'version'\d+\.\d+)" />
</identification>
<capabilities>
<capability name="browser" value="Googlebot" />
<capability name="supportsMaintainScrollPositionOnPostback" value="true" />
</capabilities>
</browser>
</browsers>

Wednesday, October 28, 2009

Clear ASP.Net Session from another ASP site

We have a 2 web app under our web site, one is asp.net and another one is asp. After user logout from the ASP web app, we need to make sure the ASP.Net site also been logout.

First we try to use Response.Cookie to delete the ASP.NET_SessionId cookie which issued by ASP.Net, but ASP will automatically encode the ASP.NET_SessionId to ASP%2ENET%5FSessionId which will add a new cookie.

So finally we use the Response.AddHeader "Set-Cookie","ASP.NET_SessionId=deleted;Path=/" to solve the problem

Write Cookie without Encode from ASP

In ASP, if you using Server.Cookie to add or change any cookie, the name and value of the cookie will be urlencode by ASP automatically. So if you call Server.Cookie("my.cookie") = "my_cookie", then on the browser, you will get my%2Ecookie = my%5Fcookie.

So you can use Response.AddHeader "Set-Cookie", "my.cookie=my_cookie" and you will get exact the name and value on your browser.

Monday, March 30, 2009

Get Windows Account For ASP.Net 2.0

Under ASP.Net 2.0, if you choose windows Authentication
1. In Web.config, set Authentication mode="Windows"
2. In IIS, set the security
2.1 Clear Anonymous
2.2 Choose Integrated Windows Authentication or
2.3 Use Client Certification and Map it to a windows account

Then in the Code, Get the windows Account as below

1. WindowsIdentity myIdentity = WindowsIdentity.GetCurrent();
WindowsPrincipal myPrincipal = new WindowsPrincipal(myIdentity);
The myPrincipal is the current account that ASP.Net running on

2. HttpContext.Current.Request.LogonUserIdentity
This is the account for the current visit account

3. HttpContext.Current.User.Identity and Thread.CurrentPrincipal.Identity are always Emtpy!