Fake binary trees
... rants, ramblings and occasional good idea ...

Yet another blog, yet another open source project

Who needs one more blog, you might ask, and i wouldn't be able to provide a good answer.

I decided to stick a toe in blog waters mostly to have a place where I can post programming-oriented reminders to myself, and hopefully to share some useful tips/code with other people.

Anyway, it coincided with the public release of Otis, my pet project, so this is also an announcement for yet another open-source project of questionable future. Otis is an open-source .NET object mapper library and you can read (and discuss) more about it on its homepage. I surely hope that some of people who somehow manage to find it in the sea of other projects will find it useful.

Posted at 18:01 on November 23, 2007
Categories: Otis   E-mail | del.icio.us | Permalink | Comments (10) | Post RSSRSS comment feed

Comments

Colin Jack wrote on February 27. 2008 at 18:52:

I wondered how I should handle cases where in the mapping you need it to create new objects on the target?

So I want to map to CustomerDTO.SalesRecipient.Name but for the mapping to work it first needs to create an instance of SalesRecipientDTO (using the default constructor) before assigning it to CustomerDTO.SalesRecipient.

zdeslav wrote on February 27. 2008 at 19:36:

This can be solved by creating a default instance in CustomerDTO:
class CustomerDTO
{
   private SalesRecipientDTO m_salesRecipient = new SalesRecipientDTO();

   public SalesRecipientDTO SalesRecipient { get... }
}

I prefer this to having a SalesRecipient property which can return null.

However, if your SalesRecipientDTO is mapped to SalesRecipient entity you don't need to do it, new instance will be created automatically (see Otis.Tests/UserDTO.cs, which has a similar Boss property which is automatically initialized by the assembler). Of course, it means that you must provide a mapping definition for SalesRecipientEntity->SalesRecipientDTO

Colin Jack wrote on February 27. 2008 at 20:51:

Unfortunately creating a default instance isn't going to work too well because the DTOs that I'm mapping to are auto-generated and I don't want to modify them.

Ta for the info on the sample, having looked at it I think I have another case, I want something like this:

   <member name="$Embedded.TheName" expression="$Name"/>

Embedded is a property of type EmbeddedClass (for the sake of argument) and what I really want is for it do this:

* Detect that the Embedded property on the targetis null and use the default constructor of EmbeddedClass to create an instance to assign.
* Copy the string value from Name on the source to TheName on the new EmbeddedClass instance.

If necessary I guess I could modify a local copy of Otis to get this behavior, if that seems like a sensible approach?

zdeslav wrote on February 27. 2008 at 22:48:

i am not sure i fully understand to what property on the source side you want to map. I suppose that customerDto.SalesRecipient.Name receives the value from customerEntity.Name ?

If this is the case, you can use the following trick (i call it a trick because it is not actually a supported scenario, but it works):

[MapClass(typeof(CustomerEntity))]
public class CustomerDTO
{
    // this will generate code which maps  CustomerDTO.SalesRecipient to the source CustomerEntity object
    [Map("source")]  
    public SalesRecipientDTO SalesRecipient;
}

[MapClass(typeof(CustomerEntity))] // map to CustomerEntity class, too
public class SalesRecipientDTO
{
    [Map("$FullName")] // map to CustomerEntity.FullName
    public string Name;
}

public class CustomerEntity // entity type
{
    public string FullName;
}

I will think about it, and try to support it in clearer manner. By the way, for debugging purposes, you can do this (must be called before cfg.BuildAssemblers, or cfg.GetAssembler):

cfg.GenerationOptions.OutputFile = @"d:\output\test.cs";
cfg.GenerationOptions.OutputType = OutputType.SourceCode;

this will generate the mapping code in file d:\output\test.cs (folder d:\output must be already created). In this case mapping assembly will not be
generated so you can't perform the actual mapping but it is helpful if you have trouble with mapping results.

Colin Jack wrote on February 27. 2008 at 22:52:

Yeah sorry, as you guessed it was customerEntity.Name ->  customerDto.SalesRecipient.Name. I'll give it a shot tomorrow and let you know how I get on.

Oh and thanks for your help Smile

zdeslav wrote on February 27. 2008 at 23:02:

Since you are the first person that i know of which uses otis (besides me, of course) I am more than glad to help Smile

Colin Jack wrote on February 28. 2008 at 11:54:

Thanks, I really like the idea of automated object-object mapping so its good to see you've already started tackling it.

Your advice seems to have sorted my original problem, I'm using XML mapping files but I think its now sorted because I get a different error. It might be worth looking at this at some stage though, having to map each class seperately isn't as neat as (if possible) using something like the <component> mapping in NHibernate.

Anyway the new error has me slightly stumped:

<Path>\Temp\6zxvkyfa.0.cs(21,84): error CS0012: The type '<Namespace>.DomainBase`1<T0>' is defined in an assembly that is not referenced. You must add a reference to assembly '<Company>.Domain, Version=1.1.2972.17006, Culture=neutral, PublicKeyToken=null'.

The class I'm mapping from does indeed inherit from the specified class and when I look at the call to csc.exe I see that the assmembly in question is not being referenced.

I had a look at the Otis code and it looks like the references could be added in AssemblerGenerator but I didn't want to do anything locally without understanding the consequences.

zdeslav wrote on February 28. 2008 at 12:38:

I think this is a genuine bug in Otis. When creating mapper assembly, for every source or target type a reference to its assembly is added. however, if they derive from type defined in some other assembly this one will not be added. I will submit a bug for this.

I don't have time to test it now, but i believe that the following workaround might work:

Add a 'dummy' mapping from any type to DomainBase type, e.g.:
<class name="Namespace.DomainBase, Company.Domain" source="Object" />

this will generate assembler for Object->DomainBase which will do nothing, but it will force mapper to reference the assembly where DomainBase is defined (i hope).

Colin Jack wrote on February 28. 2008 at 14:17:

Will do and will let you know how I get on.

zdeslav wrote on March 3. 2008 at 12:01:

new version (0.2.17) is uploaded at googlecode (http://code.google.com/p/otis-lib/downloads/list). It fixes the external assembly reference issues, and adds some minor improvements (details here: code.google.com/p/otis-lib/wiki/OtisRoadmap)

Comments are closed