Monday, September 28, 2009

Arbitrary keys & hierarchies, part 1. Introduction

Speaking about the flexibility of persistent model mapping, DataObjects.Net v3 had the following restrictions:

  • There was exactly one hierarchy with root in DataObject class.
  • Structure of identity field and type discriminator field was explicitly defined in DataObject class.
  • Only one inheritance mapping scheme -- Class Table Inheritance (with some minor exceptions) was supported.
  • Top-down development model was used. You create persistent classes, database schema is generated automatically.

Look how classic business model (Customer, Product, Order & OrderItem objects plus 2 lookup objects: DeliveryMethod & Category) might be implemented in DataObjects.Net v3:

DataObject class was the root of all. It provided identity field (type of long) and type discriminator (type of int). Developer didn’t have the possibility to define his own identity field(s) as well as custom identity provider(s), to use his own type discriminator or to choose another inheritance mapping scheme.
But I’m not going to say that it was absolutely bad or not – it really depends on concrete project. For the majority of small or medium-sized projects (startups -- DataObjects.Net v3 niche) these restrictions don’t matter at all and even make life of developer a bit easier (you don’t have to think about entity identifiers, type discriminators, hierarchy mappings and identity providers – just create your business model and let DataObjects.Net make the rest).

 

Time goes by, DataObjects.Net grew up and what was good for startups is not always good for large and complex projects. First of all, I mean that Bottom-up development model (mapping of existing database schema to persistent classes) must be supported as well. To achieve the goal we made the following modifications to persistent model mapping architecture:

  • Conception of “Hierarchy” was introduced. It represents a set of persistent classes that inherit an hierarchy root where identity fields are explicitly defined. All members of hierarchy share identity fields, inheritance mapping scheme, identity provider and type discriminator, if any. Hierarchy can contain one or more members, hierarchy depth is unrestricted. Number of hierarchies within Domain model is also unrestricted. The only restriction is that member of one hierarchy can’t belong to another hierarchy.
  • Class “Key” was introduced, it represents a set of identity fields. Every hierarchy defines its own Key structure and every Entity instance is identified by Key instance. Number of fields in Key is unrestricted, the only restriction is fields type: only primitive types can be used, plus strings.
  • In the nearest future custom type discriminators will be also supported.

Let’s update the sample persistent model, using new features of DataObjects.Net v4.

As you see, with DataObjects.Net v4 we got three independent hierarchies in the sample model:

  • Hierarchy with root in BusinessObject with identity field type of long.
  • Hierarchy with root in LookupObject with identity field type of int. This hierarchy is a good candidate for Single Table Inheritance mapping scheme.
  • Hierarchy with root in OrderItem with composite primary key (contains references to Order & Product, both type of long).

Entity type (former DataObject) doesn’t define structure of identity fields anymore. Instead, every hierarchy root defines its own Key structure. Entity class just contains the Key field where identity fields are automatically aggregated in form of Tuple. Also note, that type discriminator (TypeId field) still resides in Entity class (this will be refactored soon).
Thus, DataObjects.Net v4 now is much more closer to Bottom-up development model support then ever.

Part 2. Hierarchies

CodeProject

Sunday, September 27, 2009

Blogger & Windows Live Writer

If you are new to Windows Live Writer then you may face with the following problem: when you try to insert images or video into your post via Windows Live Writer you may get the error 403 (Forbidden).

The solution is simple: try going to http://picasaweb.google.com, log in as the same Google Account you use to post to Blogger, and create an empty album called "Windows Live Writer". Then try posting from Windows Live Writer again.

It worked for me.

Tuesday, September 22, 2009

System.Configuration & Enum case-insensitivity

Yesterday one of DataObjects.Net 4 customers made a request for the following enhancement: he wrote it would be much more usable if one could write case-insensitive Enum options in DataObjects.Net configuration section. So instead of standard case-sensitive:

<domain upgradeMode="Recreate"/>

he suggested that we should implement support for case-insensitive values also, e.g.:

<domain upgradeMode="recreate"/>

As IMO the suggestion was really usable and I thought it was easy to implement, I decided to make it ASAP. I knew that there are two ways of parsing Enum values: case-sensitive and case-insensitive – both of them are provided by static Enum.Parse member. I thought that we were using case-sensitive one. So I went to Xtensive.Storage.Configuration namespace and found the corresponding code. It was the following:

[ConfigurationProperty(UpgradeModeElementName, IsRequired = false, 
DefaultValue = DomainUpgradeMode.Default)]
public DomainUpgradeMode UpgradeMode
{
  get { return (DomainUpgradeMode)this[UpgradeModeElementName]; }
  set { this[UpgradeModeElementName] = value; }
}

As you see, DataObjects.Net 4 doesn’t responsible for Enum value parsing as it expects already parsed Enum value, but not initial string from app.config. But who is responsible for Enum value parsing then? Let’s dig into the System.Configuration assembly.

With .NET Reflector I found that System.Configuration.GenericEnumConverter is the place where string is converted into Enum value. But unfortunately it uses case-sensitive method of parsing strings and there is no way to change this behavior. It is hard-coded by one of microsofties. This means that all developers that use System.Configuration are to write only case-sensitive Enum values in their project configuration files.

 

The alternative way is to convert all Enum-based properties into string-based ones and parse them manually during ConfigurationElement –> Configuration conversion routine. So we should change the above-mentioned property into the following one:

[ConfigurationProperty(UpgradeModeElementName, IsRequired = false, 
DefaultValue = "Default")]
public string UpgradeMode
{
  get { return (string)this[UpgradeModeElementName]; }
  set { this[UpgradeModeElementName] = value; }
}

The second step is to add case-insensitive Enum-parsing logic into DomainConfigurationElement.ToNative() method:

domainConfig.UpgradeMode = (DomainUpgradeMode) Enum.Parse(
  typeof (DomainUpgradeMode), UpgradeMode, true);

That’s it!
Certainly, the same modification is made to all Enum-based configuration properties.

P.S.
Thanks to Ivan Galkin for the bright idea.

CodeProject

Friday, September 18, 2009

Persistent interfaces, part 2

In the previous post I described existing requirements to persistent interfaces. Now let’s talk about interface queries.

In most cases interface query is no more than a union of queries for every interface implementor, e.g., if we have the following Domain model,

public interface IAnimal : IEntity
{
  [Field]
  string Name { get; set; }
}

[HierarchyRoot]
public class Dog : Entity, IAnimal
{
  [Field, Key]
  public int Id { get; private set; }

  [Field]
  public string Name { get; set; }
}

[HierarchyRoot]
public class Cat : Entity, IAnimal
{
  [Field, Key]
  public int Id { get; private set; }

  [Field]
  public string Name { get; set; }
}

then Query<IAnimal>.All (query for all instances that implement IAnimal interface) will be translated into the following SQL code:

SELECT a.Id, a.TypeId, a.Name FROM Dog a 
UNION
SELECT b.Id, a.TypeId, b.Name FROM Cat b

As I mentioned earlier, to make this work we require that:

  • all corresponding fields in all implementors of an interface have equal SQL type;
  • all implementors of an interface have equal key structure;
  • all implementors of an interface have unique keys within interface implementors domain. Last one is achieved in most cases by sharing the same key generator instance for all hierarchies that have implementors of concrete interface.

DataObjects.Net 4 supports indexed views/materialized views for persistent interfaces. This could be achieved by marking interface type with MaterializedView attribute, e.g.:

[MaterializedView]
public interface IAnimal : IEntity
{
  …
}

In this case DataObjects.Net 4 will investigate the possibility of creating indexed view/materialized view in currently used Storage. If Storage supports the feature then corresponding indexed view will be created; otherwise, a regular table will be created and its contents will be updated by DataObjects.Net 4 itself during persist routine.

P.S. Indexed view support is in development stage for now as well as persistent interfaces support.

CodeProject

Tuesday, September 15, 2009

Shortcut to TypeInfo

There are two ways how to get the corresponding TypeInfo instance for a persistent Type.

First way is straightforward:

TypeInfo typeInfo = Domain.Model.Types[typeof (Animal)];

The second is much shorter, it is powered by .Net Framework 3.5 extension methods feature:

TypeInfo typeInfo  = typeof (Animal).GetTypeInfo();

or

TypeInfo typeInfo = typeof (Animal).GetTypeInfo(Domain);

Monday, September 14, 2009

Persistent fields accessibility levels

Havasvölgyi Ottó pointed out that DataObjects.Net 4 manual contains code samples with public persistent fields only. I’m going to remedy this deficiency. DataObjects.Net 4 supports all kind of persistent fields: private, internal and public, e.g.:

[HierarchyRoot]
public class MyEntity : Entity
{
  [Field, Key]
  public int Id { get; private set; }

  [Field]
  public string PublicField { get; set;}

  [Field]
  internal string InternalField { get; set; }

  [Field]
  private string PrivateField { get; set; }
}

Moreover, you can combine accessibility modifiers as you like, e.g.:

[Field]
public string ReadOnlyField { get; private set; }
CodeProject

Thursday, September 10, 2009

Referencing objects finding task

IMO, The feature to find objects that reference a concrete Entity instance is essential for any O/RM. So DataObjects.Net 4 also has the feature but is it really usable for developers? I’m afraid, no.

What DataObjects.Net 4 can offer for now:

public static class ReferentialHelper
{
  public static IEnumerable<Entity> FindReferencingEntities(Entity entity);

  public static IEnumerable<Entity> FindReferencingEntities(Entity entity, AssociationInfo association);
}

This assumes that developer must know that somewhere there is a static class ReferentialHelper that is responsible for this functionality. Without such knowledge developer has zero chances to achieve his goals in finding referencing entities. Too bad both for developer and for our product.
Moreover, this pattern doesn’t cover the scenario when developer wants to receive additional information about concrete property that holds a reference to specified Entity instance.

Luckily, we have a chance to make it more usable. With the help of our community, we are inventing the updated referencing objects finder pattern:

1. Structure, that describes a reference, has been introduced:

public struct ReferenceInfo
{
  public Entity ReferencingEntity { get; private set; }
  public Entity ReferencedEntity { get; private set; }
  public AssociationInfo Association { get; private set; }
}

It contains enough information about concrete reference: the AssociationInfo instance that describes connection between 2 objects, and references to objects at both ends of the connection.

2. ReferentialHelper method’s signature has been changed to the following:

public static IEnumerable<ReferenceInfo> FindReferencingObjects(this Entity target)
public static IEnumerable<ReferenceInfo> FindReferencingObjects(this Entity target, AssociationInfo association)

As you see, these extension methods have been targeted the Entity type. The reason for extension methods instead of regular ones is obvious: finding referencing entities is not the responsibility of Entity class. But in order to make them more usable for developers we should somehow bind them to some type that are well-known in DataObjects.Net 4 domain context and are familiar to developer. This type could be Session, Domain or Entity. So after some discussion we choose Entity type because the first place to search for such kind of methods is the Entity type itself.

3. Another extension method that quickly answers to the question “Is Entity instance referenced at all? “ has been added:

public static bool IsReferenced(this Entity target)

 

So, I think that from time to time we should make a review of existing code base in order to find such kind of issues and refactor them into more usable and understandable code.

P.S.
Special thank to Havasvölgyi Ottó – one of the most valuable DataObjects.Net partner and contributor for his bright ideas and constant willingness to participate in numerous DataObjects.Net-related discussions.

CodeProject

Wednesday, September 9, 2009

Persistent interfaces, part 1

First of all, interfaces can’t be persistent. An interface represents no more than a contract that must be implemented by some type. It is an abstract term. Speaking about persistent interfaces within DataObjects.Net 4 project we mean that we want OR/M to support the following scenarios (they are our goals):

1. Reference:

public class Animal {
  public IOwner Owner { get; set; }
}

2. Collection of references:

public class Person {
  public EntitySet<IAnimal> Pets { get; private set; }
}

3. Query:

Query<IAnimal>.All.Where(a => a.Owner = myOwner);

 

In order to achieve the goals we should keep in mind the following facts:

1. DataObjects.Net 4 supports multiple persistent hierarchies. This means that one may define hierarchies with different key structure, e.g.:

[HierarchyRoot]
public class Cat : Entity, IAnimal
{
  [Field, Key]
  public int Id { get; private set; }
}

[HierarchyRoot]
public class Dog : Entity, IAnimal
{
  [Field, Key]
  public Guid Id { get; private set; }
}

In Cat class the key field is of type Int32, but in Dog class it is of type Guid. As any reference field is stored as a copy of referenced object key field(s), we can’t handle a reference to IAnimal because we can’t decide which type of reference field for underlying SQL table to choose.

public class Person : Entity
{
  public IAnimal Pet { get; set; }
}

So the first rule is: all persistent types that implement given interface must have equal key structure. Otherwise, reference to interface implementor can’t be persisted and query for interface implementors can’t be executed.

2. DataObjects.Net 4 supports various types of key generators. Key generator acts as a source of unique keys for one or more hierarchies. It can be shared between hierarchies if they have equal key structure. If key generator is not specified manually, default key generator implementation is used but this is true only for simple key structure, i.e. key consists of one primitive field.
Going back to interface support: if two types has equal key structure but have different key generators then instances of these types might have equal keys. If so, when attempting to resolve the reference property we can’t decide which instance is referenced: instance of Dog type or instance of Cat type. This is true for the case when key doesn’t contain type discriminator (TypeId in case of default type discriminator in DataObjects.Net 4).

So, the second rule is: all persistent types that implement given interface must have the same key generator if key doesn’t contain type discriminator. Otherwise, we can’t decide which type to construct while trying to resolve a reference property of interface type.

3. Interface could be implemented explicitly. Entity class can contain for example Name property and explicitly implemented IHasName.Name property. In this case when querying for IHasName interface we must use explicitly implemented property instead of regular one. This means that we must detect this cases and provide the information how interface is mapped to its implementor.

CodeProject

Salute

Hello all,

My name is Dmitri Maximov and I work for Xtensive.com in DataObjects.Net development team. Some tasks that we face during the development process are quite interesting and uncommon. I’ll try to describe some part of them here as well as ideas, concepts and best practices.

P.S.
Sorry for my poor English. As you may understand, I’m not a native English-speaking person. But I’m doing my best.