Convert Validator from EVil to DataAnnotation

We were using Evil Validation library for a while, works OK, but doesn’t support WPF/Silverlight and MVC very well. Fortunately, it’s not too hard to convert it to DataAnnotations.

Evil code example:


    class EmailAddress
    {
        public EmailAddress(string value)
        {
            Value = value;
        }

        [ValidateRegex(@"\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}", "Invalid recipient email address")]
        public string Value { get; set; }

    }

        public static bool ValidateEmailAddress(string emailAddress)
        {
              return ((new EmailAddress(emailAddress)).IsValid());
        }

To DataAnnotation:


    class EmailAddress
    {
        public EmailAddress(string value)
        {
            Value = value;
        }

        [RegularExpression(@"\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}", ErrorMessage = "Invalid recipient email address")]
        public string Value { get; set; }

    }

       public static bool ValidateEmailAddress(string emailAddress)
        {

            EmailAddress emailToValidate = new EmailAddress(emailAddress);

            // Watchout the last bool flag, indicate whether validate all properties. (What else? just modified one?)
            return Validator.TryValidateObject(emailToValidate, new ValidationContext(emailToValidate, null, null), null, true);

            // For one property model, we can also use this syntax.
            ValidationContext validationContext = new ValidationContext(emailToValidate, null, null);
            validationContext.MemberName = "Value";
            return Validator.TryValidateProperty(emailAddress,
                validationContext
                , null);

        }
Advertisements

Reading appSettings from config

I noticed I might have googled for this same tech question more than 5 times, so this time I wrote my own post in case.

Reading appSettings from config should be as simple as:


var numberInString = ConfigurationManager.AppSettings["MaxNumberOfSearchReturnPosts"];
return numberInString == null ? 0 : Int32.Parse(numberInString);

While on MSDN we can find another post for web.config, which supports reading from runtime! Real web hosting environment.


System.Configuration.Configuration rootWebConfig1 =
System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("/MyBlogSite");
if (rootWebConfig1.AppSettings.Settings.Count > 0)
{
var numberInString = rootWebConfig1.AppSettings.Settings["MaxNumberOfSearchReturnPosts"];
return numberInString == null ? 0 : Int32.Parse(numberInString.Value);
}
return 0;

Note, passing null argument for OpenWebconfiguration() means reading from root path of your host web site. MSDN Document doesn’t state this very clearly.

Looks this this method can also read from a different site! Cool.

 

 

 

RowFocusChanged Event in datagrid?

I couldn’t find this event in dataGridview control, it seems in asp.net world they have a selectedIndexChanged event but winForm doesn’t. The closest one might be CurrentCellChanged, my translate code is:


        private int _currentRowIndex = -1;

        private void dataGridView1_CurrentCellChanged(object sender, EventArgs e)
        {
            if (dataGridView1.CurrentRow == null) return;

            if (_currentRowIndex == dataGridView1.CurrentRow.Index) return;

            // I put my processing code in this method.
            dataGridRowChanged(sender, e);

            _currentRowIndex = dataGridView1.CurrentRow.Index;
        }

Which looks very stupid. To make it a little bit nicer, I can use Concurrency Manager instead.

 set {
                this.dataGridView1.DataSource = value;

                dataGridCurrentRowChanged(null, null);

                // Setup Concurrency Manager.
                dataGridView1.BindingContext[dataGridView1.DataSource]
                    .CurrentChanged += new EventHandler(dataGridCurrentRowChanged);

            }

Phew! UI coding is hard.

NHibernate and Stored Procedure, Part2, dealing with orginal values

In part1, I can easily add those original values in the hbm mapping xml files. This works OK for web forms, and also no problem for one time update in win forms. But when I tried to make a repeatable update in win forms, the problem appears.

The original values in Entity objects doesn’t sync! When I called a DAO.commit(), all the changes have been made into database, and the changed columns/properties also still kept in entity objects, you think those original properties will be refreshed? No, not until you clean up the NHiberate cache manually.

Object: ID,
property: originalvalue (1)
property: realvalue (1)

Set realvalue = 2, then call Dao.CommitChanges()

Object: ID,
property: originalvalue (1) {This didn’t update! Still keep the previous original value, should be the current/original value which is 2.}
property: realvalue (2)

The solution I got is, after Dao.CommitChanges(), do a cache clean up manually.

myEntityDao.StartTransaction();
myEntityDao.SaveOrUpdate(view.MyEntityToUpdate);
myEntityDao.CommitChanges();

myEntityDao.Evict( view.MyEntityToUpdate );

This made my winForm repeatable update working. An extra work is, I have to recall the initview() in UI layer, because UI needs to re-load this updated object from Dao after evict().

Again, this is a win form only issue, web form doesn’t have this problem because each page is reloaded after every commit.

Databinding nested properties

This will be a common problem when using NHibernate. You want to show something on datagrid like EntityA.EntityB.EntityC.property1, but you can’t. Somebody said it’s impossible from MicroSoft forum.

A guy from Oslo got a solution by extending standard BindingSource component, which is too complex to me. The more important thing is, don’t put too many code in UI. Also, I couldn’t event open his original post on codeproject.

When I tried to expose those nested properties in parent Entity Object, I couldn’t figure out the NullReferenceExpection. Too many magics happen in NH’s ORM process.

Mabsterrama’s front-end re-formatting idea works great so far, even it’s also a UI layer solution.

Michal Talaga gave a explanation why datagrid doesn’t have this problem when using SqlDataSource but in objectDataSource. His solution is almost the one I want, BO/Domain layer work around, kind of ‘object view’. The real objectView project is based on MS’s Enterprise Library. (What’s that?)

Actually, expose the child/nested properties isn’t that hard. Only one line of code.

        public virtual string Location   {    get { return site.CurrentName; } }

But this againsts OO. I wish dotNet35+ will fix this weakness soon.

 

Here is another solution in UI layer: coding in InitializeRow event.

Split NHibernate Domain Objects Needs Loading Multipule HBM Assemblies

With tens of NHibernate classes and hbm files generated, I realized it’s time to split them to different project/assembly. After finished split, I got the familiar unmapped class error, same one when I forgot set hbm file to embeded resource. By copying those hbm files back to the original project, this problem went away. But, shouldn’t assembly and class files always sit in the same project?

Definitely yes, something must be wrong here. I looked into the framework/demo (from Billy McCafferty). The problem is in NHibernateSessionManager, the Configureation.AddAssembly only takes one filename. So I made a small change:

App.config
<appSettings>
<add key=”HBM_ASSEMBLY” value=”Assembly1, Assembly2″/>
</appSettings>

NHibernateSessionManager.InitSessionFactory()

Configuration cfg = new Configuration();
string assembly = ConfigurationManager.AppSettings[“HBM_ASSEMBLY”];

if (assembly.Contains(“,”) )
{
foreach (string a in assembly.Split(‘,’))
cfg.AddAssembly(a);

}else{
cfg.AddAssembly(assembly);
}

sessionFactory = cfg.BuildSessionFactory();

Wala, back to all green now!

For configuration, I am using basic style, adding everything into <nhibernate> section then set the assembly in <appSettings>.

<appSettings>
<add key=”HBM_ASSEMBLY” value=”MyAssembly”/>
</appSettings>
<nhibernate>
<add key=”hibernate.connection.provider” value=”NHibernate.Connection.DriverConnectionProvider”/>
<add key=”hibernate.dialect” value=”NHibernate.Dialect.MsSql2000Dialect”/>
<add key=”hibernate.connection.driver_class” value=”NHibernate.Driver.SqlClientDriver”/>
<add key=”hibernate.connection.connection_string” value=”…” />
<add key=”hibernate.connection.isolation” value=”ReadCommitted”/>
<add key=”hibernate.show_sql” value=”true”/>
<add key=”hibernate.mapping.assembly” value=”MyAssembly”/>
</nhibernate>

I tried to add hibernate.mapping.assembly key into nhibernate section, didn’t work. But in Billy’s new sessionFactory configuration style, (which can support multiple db connections)

App.config:
<nhibernateSettings>
<sessionFactories>
<clearFactories />
<sessionFactory name=”northwind” factoryConfigPath=”C:\absolute_path\NorthwindNHibernate.config” isTransactional=”true” />
</sessionFactories>
</nhibernateSettings>

NorthwindNHibernate.config:
<hibernate-configuration xmlns=”urn:nhibernate-configuration-2.2″ >
<session-factory name=”northwind”>
<property name=”dialect”>NHibernate.Dialect.MsSql2005Dialect</property>
<property name=”connection.provider”>NHibernate.Connection.DriverConnectionProvider</property>
<property name=”connection.driver_class”>NHibernate.Driver.SqlClientDriver</property>
<property name=”connection.connection_string”>Data Source=.\SQLExpress;Database=Northwind;Integrated Security=SSPI;</property>
<property name=”connection.isolation”>ReadCommitted</property>
<property name=”default_schema”>Northwind.dbo</property>
<!– HBM Mapping Files –>
<mapping assembly=”EnterpriseSample1.Core” />
</session-factory>
</hibernate-configuration>

The mapping assembly can be taken automatically by:

ISessionFactory sessionFactory = (ISessionFactory) sessionFactories[sessionFactoryConfigPath];

Unfortunately, I tried to add multiply assemblies in this session factory configuration style, also failed.

I downloaded the source code of NHibernate, in protected Configuration DoConfigure(XmlDocument doc):

……
foreach (XmlNode mapElement in sfNode.ChildNodes)
{
string elemname = mapElement.LocalName;
if (“mapping”.Equals(elemname))
{
XmlAttribute rsrc = mapElement.Attributes[“resource”];
XmlAttribute file = mapElement.Attributes[“file”];
XmlAttribute assembly = mapElement.Attributes[“assembly”];
if (rsrc != null)
{
……
}
else if (assembly != null)
{
log.Debug(name + “<-” + assembly.Value);
AddAssembly(assembly.Value);
}

Which proofs NHibernate always assume each factory can only have one assembly, even you can load more than one by calling cfg.AddAssembly() directly. But I tried call cfg.AddAssembly() with cfg.Configure() together, didn’t work, all kids of errors based on different combinations. I gave up finally.

Meanwhile, I don’t like writing absolute path to the session factory in configuration. I can change it to relative one, but needs to set the compile action to “content” to avoid copying it to debug folder manually then.

Evaluation of Code Generator

CodeSmith 4.1
Pros:
– UI very good.
NTiers template looks pretty cool, using Enterprise library and Entity Provider pattern. But this is too complex for me. I still prefer MVP + NHibernate.

Cons:
– NHibernate template doesn’t support View. When using table I got a error said can not find primary key in our database enviorment.

SmartCode 2.0
Pros:
– Opensource.
– Output can include DaoFactory, DomainObject (ancestor), Interface of DomainObject, almost every thing in DAL. I think Billy McCafferty is using this tool.

Cons:
– Datasource configuer window is not very user friendly.
– Couldn’t regconise our no-primary key table.
– View list is not sorted. Very difficult to use.
– Changing template has to go through rebuild the project provided as source code. No wonder it’s open source. This work is annoying.

MyGeneration 1.2.07
Pros:
NHibernate template can support view, including no-primary key view.

Cons:
– UI too simple.
Not opensource. OpenSource started from June 2007.

Most of our tables don’t have primary key instead we are using identity column. (Our DBA just like this way, I don’t know why.) Because SmartCode is the only one has source code, so I can modify it to make it meet my requirements.

I followed the templates of MyGeneration, then I realized that even MyGeneration failed to get my identity column. But the thing is MyGeneration didn’t quit, instead it printed out a line:

<!– could not find a primary key for this table/view. NHibernate requires an ‘id’ element, so you’ll have to define one manually. –>

It’s not hard to change SmartCode to follow the same routine. Only minutes of work. Theoretically, I could make the same change to CodeSmith Template, nut when I tried to change TableSchema to ViewSchema to enable ViewPicker instead of TablePicker in CodeSmith’s NHibernate Template, it turns out CodeSmith cannot change type ‘SchemaExplorer.ViewSchema’ to ‘SchemaExplorer.TableSchema’!

None of those tools can generate many-to-one relationship in class or hbm files based on our view-only database, which makes sense to me, our table/view don’t have foreign key or reference.

Obviously, NHibernate hates DBA , and vice versa.

After closely looking into the generated templates, I start to admire MyGeneration. Their class file looks more professional, with region tag and two flags: isDeleted and isChanged (don’t know how to use yet. But isn’t that thoughtful?)

Yes, I can change the templates in smartcode to get the exactly same result, just need some rearrangement work. But I don’t want spent more time in it. Tools are good, but don’t addict into too much.