CRM Development

Solutionist has designed the architecture for a new CRM system for use in the medical imaging industry.

Tuesday 10 August 2010

Quartz.NET

  I haven't got round to trying Spring.NET yet, but had a play with another Java-to-.NET port: Quartz.NET.

I'm sure that there are other posts/tutorials about how to get it working, but I'll add in my twopennyworth as a step-by-step account of what I did.

1. Downloaded latest version of Quartz.NET - 1.0.2
2. Using VS2010, I created a Web Project and added references to the Quartz and Common.Logging dll's that are in the bin directory
3. For good measure I also added a reference to the log4net dll - I would be using this in the Quartz job.
4. For log4net config, I kept this separate from the application and placed a log4net.config file in project. I then referenced this in Global.asax as follows:

log4net.Config.DOMConfigurator.ConfigureAndWatch(new System.IO.FileInfo(AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "log4net.config"));

My log4net config file looked like:





<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <root>
    <level value="DEBUG" />
    <appender-ref ref="RollingFileAppender"/>
  </root>

  <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="logs\Logfile.log" />
    <appendToFile value="true" />
    <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
    <rollingStyle value="Composite"/>
    <datePattern value="yyyyMMdd"/>
    <maxSizeRollBackups value="10" />
    <maximumFileSize value="1000KB" />
    <staticLogFileName value="true" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%d [%t]%-5p %c [%x] - %m%n" />
    </layout>
  </appender>

</log4net>



5. Ran the database script (for SQL server) to create the relevant tables.

6. Updated the Web. Config file as follows:




  <configSections>

    <section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
  </configSections>







  <quartz>
    <add key="quartz.scheduler.instanceName" value="CommerceScheduler" />
    <!-- Configure Thread Pool -->
    <add key="quartz.threadPool.type" value="Quartz.Simpl.SimpleThreadPool, Quartz" />
    <add key="quartz.threadPool.threadCount" value="10" />
    <add key="quartz.threadPool.threadPriority" value="Normal" />
    <!-- Configure Job Store -->
    <add key="quartz.jobStore.misfireThreshold" value="60000" />
    <!--<add key="quartz.jobStore.type" value="Quartz.Simpl.RAMJobStore, Quartz" />-->
    <add key="quartz.jobStore.type" value="Quartz.Impl.AdoJobStore.JobStoreTX, Quartz" />
    <add key="quartz.jobStore.dataSource" value="default" />
    <add key="quartz.jobStore.tablePrefix" value="QRTZ_" />
    <add key="quartz.jobStore.clustered" value="true" />
    <add key="quartz.jobStore.lockHandler.type" value="Quartz.Impl.AdoJobStore.UpdateLockRowSemaphore, Quartz" />
    <add key="quartz.jobStore.driverDelegateType" value="Quartz.Impl.AdoJobStore.SqlServerDelegate, Quartz" />
    <add key="quartz.dataSource.default.connectionString" value="Data Source=Server_Name;Initial Catalog=quartz;Integrated Security=True" />
    <add key="quartz.dataSource.default.provider" value="SqlServer-20" />
    <add key="quartz.jobStore.useProperties" value="true" />
         </quartz>



7. Created a test job:


public class QuartzJob1 : IJob
{
  protected static log4net.ILog log =      log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

  void IJob.Execute(JobExecutionContext context)
  {
    log.Debug("QuartzJob1 run");
  }
}

8. For testing purposes created a class with the following static method.

public static void InitQuartz()
{
    // construct a scheduler factory
    ISchedulerFactory schedFact = new StdSchedulerFactory();

    // get a scheduler
    IScheduler sched = schedFact.GetScheduler();
    sched.Start();

    // construct job info
    JobDetail jobDetail = new JobDetail("myJob", null, typeof(QuartzJob1));
    // fire every hour
    Trigger trigger = TriggerUtils.MakeMinutelyTrigger();
    // start on the next even hour
    trigger.StartTimeUtc = TriggerUtils.GetNextGivenMinuteDate(DateTime.UtcNow, 1);
    trigger.Name = "myTrigger";
    sched.ScheduleJob(jobDetail, trigger);
}

9. Global.asax added a call to InitQuartz()

10. Ran the application.

You should see a log message being written out every minute.
And you should see entries in the
QRTZ_SIMPLE_TRIGGERS and QRTZ_FIRED_TRIGGERS tables.




Friday 6 August 2010

RIA Services and ASP.NET

I've been looking at WCF RIA services for a new project - and looking at seeing if I can use the DomainService on an ASP.NET page directly.

This gets you to a half-way house situation where you have your middle-tier, but don't have to learn Silverlight just yet. It's not immediately obvious where to start, but you need to get hold of the RIA services toolkit (for SL4... I'm working with VS2010) - which then gives you a DomainDataSource available for use on .aspx pages.

So.... created my DomainService with a couple of related entities.
pulled in a couple of DomainDataSources for the master and detail queries and hooked up a grid for the master query and DetailView for the detail.

The basic concept worked fine!

But.. then I tried to enable paging and sorting in the master and detail view and got this error

The method ‘Skip’ is only supported for sorted input in LINQ to Entities. The method ‘OrderBy’ must be called before the method ‘Skip’.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.NotSupportedException: The method ‘Skip’ is only supported for sorted input in LINQ to Entities. The method ‘OrderBy’ must be called before the method ‘Skip’.
Hmmm....

It turns out (thanks to this link) that you need to add some default ordering to your query methods in the DomainService. This enforces the ordering before any other modifier.

Wednesday 4 August 2010

Training

Running an ASP.NET training course recently got me thinking about the range of skills someone needs nowadays working in IT.

The students came from a wide range of backgrounds - some with more or less development experience, but the range of topics we were covering on was huge:
  • HTML
  • Javascript
  • C#
  • OO concepts
  • XML
  • Security
  • Database access
  • Visual Studio IDE (a course in itself!)
... and we're not even talking about particularly complex sites.
If you are talking about complex enterprise applications, then you're into services (of varying kinds), AJAX, async messaging, enterprise integration, design patterns, various competing Microsoft and Java/J2EE technologies, etc, etc

I've picked this up over many years in the industry - these students are faced with a bewildering array of information to absorb at the outset. Unfortunately, all too often, potential employers want to see the "finished" article - but it doesn't happen overnight.

I wish them well on their chosen career path - if they appreciate that being in IT means being willing to look at new ideas and continue learning, then they're a good part of the way there to appreciating what IT is as a profession.

.NET IoC

Blogging is a new departure for me, but now I've started my own company (Solutionist) - it seemed like a good time to start... so here goes...

I've been working a lot with .NET technologies lately and have been trying to pull across my
existing Java/J2EE knowledge as a frame of reference.

Something that was sorely missing was Dependency Injection - I'd become fairly hooked on Spring for that and was really missing it in Microsoft World. So, finding Spring.NET and Microsoft's own Unity framework has come as a bit of a relief. Now it's just a case of seeing which one "fits" better.

Hopefully, over the next few days as I try them both out, I'll write it up.