This is a migrated thread and some comments may be shown as answers.

Extending OA generated class

13 Answers 151 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Chris
Top achievements
Rank 1
Chris asked on 16 Sep 2011, 01:48 AM
Hi, all,

I have a seemingly simple question but not so simple to get it right.

I have a OA generated class and I would like to extend the functionality.
I considered 3 different options.

1.  I could extend though "partial" definition since OA generated class are "partial".
 I need to extend them from different assembly(with different namespaces), therefore it won't work.

2. Make OA generated class as a member of a derived class and use "Property" to acess OA class members..
For example, 
 
public MyClass
 {
   OAClass _oaClass;
    
    public MyClass(OAClass oaClass)
    {
        _oaClass = oaClass;
     }
    public Id 
    {
         get { return _oaClass.Id;}
          set { _oaClass.Id = value; }
    }
}

But I have quite large tables and making Property wrappers for each OA class members can be quite cumbersome.

3. Derive from OA generated class, like the following.

public MyClass : OAClass
{
    // override properties here if necessary, otherwise it will default to OAClass memebers
   ....
}

This seems to be the easiest option as I can access OA generated class' memebers without doing anything.
However, I'm not sure I how make an instance of the derived class from the OA generated class properly.
I tried type-cast OAClass to MyClass but it won't work either.
I could just copy each Properties of OAClass to MyClass using reflection but then link to DB will be broken and I will not save changes back to DB.

Is Option 2 the only choice? 
How can I make option 3 work? 
Or are there any other recommendations?

Thanks alot!








13 Answers, 1 is accepted

Sort by
0
Kevin
Top achievements
Rank 1
answered on 16 Sep 2011, 12:08 PM
Hi Chris,

You can't downcast in .Net, AFAIK - implicit creation of a derived type from a base type doesn't work, because (by definition) the derived type is more derived and has more properties... there's no way to know what to do with those properties, so your derived type state would be unpredictable. You can create a constructor on your derived type which accepts the base type as an argument - and sets properties appropriately, either by calling the base constructor or by reflecting properties - but that probably doesn't solve the DB link issue you mention (at least without a lot of work).

Option 2 works, as you already note - but option 1 would be a "better" design I think. Could you not simply create a second assembly with the same namespace as the OA classes and extend them that way? If what you're trying to do is add properties to the OA classes, then those properties really should be defined in the same namespace irrespective of the containing assembly.

0
Chris
Top achievements
Rank 1
answered on 16 Sep 2011, 05:45 PM
Thanks Kevin,
I'm leaning toward option 1 at this point. But I want to point out that you can't define "partial" class spaning multiple assemblies.
It forces me to have all my extended classes in single assembly which is not good.

Anyway, I was hoping that Option 3 works. If OA is smart enough, the base class has be inherited and instantiate the inherited class and let us work on it.
I think it's possible through using "interface" and "reflection", no?



0
Kevin
Top achievements
Rank 1
answered on 16 Sep 2011, 06:24 PM
Hi Chris,

Sorry, you're right - had assumed you'd be able to reference a class library from another class library and compile them into the same output assembly (as with COM interop types) but evidently it's not possible with managed code.

One method that might do what you want is to use extension methods, like so:

Assembly OAClasses.dll:

namespace OAClasses
{
    public class OAClass
    {
        public bool ReturnTrue()
        {
             return true;
         }
     }
 }


Separate assembly:
namespace OAClassExtensions
{
    public static class OAClassExtensions
    {
        public static bool ExtraMethod(this OAClass foo)
        {
            return false;
        }
 
    }
}

That assembly needs a reference to the one containing OAClass, obviously. You can then do this:

using OAClassExtensions;
 
namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
 
            OAClasses.Class1 c1 = new OAClasses.Class1();
 
            bool b = c1.ReturnTrue();
            bool c = c1.ExtraMethod();
        }
    }
}

Unfortunately I don't think you can have extension properties...



Your problem with option 3 is the fact that you can't cast a base type to a derived type, and nor can you define conversion operators. You'd have to do something like:
public class MyClass : OAClass
{
public MyClass() : base() {}
public MyClass(int foo) : base(foo) {}
}

which would call the OAClass default constructor when MyClass is instantiated and set up the relevant properties, while still exposing all the OAClass properties and methods via MyClass (unless you override them).

The awkwardness comes with static factory methods...

public static OAClass GetOAClass(int foo)
{
return new OAClass();
}

...is always going to return an OAClass and there's no easy way (bar reflecting each property and setting them by hand) to convert that to a MyClass instance. You're reliant on the consumer of your class remembering to call MyClass.GetMyClass() instead of OAClass.GetOAClass() instead, which is never a good idea.

Kev

0
Chris
Top achievements
Rank 1
answered on 17 Sep 2011, 12:00 AM
Thanks again Kevin,
I'll give option 1 a try and stop by if there are other problems I find.

Cheers!
0
Ivailo
Telerik team
answered on 21 Sep 2011, 01:09 PM
Hi Chris,

To provide you with some input on that matter, as per Telerik OpenAccess ORM best practises:

1. Generally, the best option is to use partial classes in order to extend the functionality. However, this is indeed not applicable in case you need the additional methods/properties to be defined in another assembly.

2. If you want to extend the functionality in another assembly, the first option to consider is extension methods.

3. If you want to have properties in your other assembly as well, there are two options:
 3.1. You can inherit or compose (as private fields) the Domain Classes. The disadvantage is that you will have to write code to copy all the properties you will need from the parent classes to the child classes.
 3.2. You can compose the Domain Classes as public properties instead of private - this way you will not have to copy all the properties, you will assign an entire object at once. For example:
public class Customer
{
   public string Name {get;set;}
}
 
public class ExtendedCustomer
{
   public byte FirstName {get;set;}
 
   public byte SecondName {get;set;}
 
   public Customer CustomerFields {get;set;}
}

So you do have different options, depending on your own design decision.


I am looking forward to hearing from you.


Greetings,
Ivailo
the Telerik team

Thank you for being the most amazing .NET community! Your unfailing support is what helps us charge forward! We'd appreciate your vote for Telerik in this year's SQL Server Community Awards. We are competing in TWO categories and every vote counts! VOTE for Telerik NOW >>

0
Chris
Top achievements
Rank 1
answered on 22 Sep 2011, 02:06 AM
Hi,
I decided to use option 1 (extend through "partial" in the same assembly) and I think it's the simpliest.

The next "quest" is to get the inheritance to work.
I have DB with flat hierarchy and trying to match it with inheritance is quite difficult problem.
I tried "Horizontal" in the Entity Model but there are lots of problem I encountered. (Round-trip won't work as expected, perhaps it won't work by design)
Instead of waiting for the proper implementation, I found a great method that can map flat DB to hierarchical classes but I need some help from Telerik to make it usable.

Here is the setup

1. pre-defined Class structure. (simplfied for illustration)
    
    public class Animal 
    {   
        public virtual string Name {get; set;}
    }

    class partial Dog : Animal
    { }

    class partial Bird : Animal
    {   
        public virtual bool CanFly {get; set;}
    }

2. DB table
    
    Bird : Name, CanFly
    Dog : Name

3. Now I generate Model from DB and the auto-generate DB class looks like the following.

 class partial Dog 
 { 
        public virtual string Name {get; set; }
 }

class partial Bird

    public virtual string Name {get; set;}
    public virtual bool CanFly {get; set;};
}

even though the generated class doens't know about hierarchy, my partial class defines it, therefore it will compile with no problem.

However, to make the properties in the generated class to work, it needs be "override", not "virtual".
If I change it to "override", everything works beautifully.
I now  hand-editing them to "override" but they get overwritten everytime Model changes and it's amost impractical.

So, I need help from you and it can be done by two different ways.

1. When generating properties, change "virtual" to "override"  You cannot just change everything to override because, if there is no property already defined, it will error. It needs to look at if there are same properties already defined by user, and change to "override".
I think the reason why you made a partial class is to make it extendable and I think that's the proper behavior anyway.

2. Fix inheritance round-trip design. I'm not sure what needs be done but regenerating Models from flat DB, will loose inheritence and I think it can't be fixed without some metadata stored in the DB. 

#2 can be quite challenging and I don't expect it to be fixed soon, however, #1 is quite doable and supporting inheritance would be super easy.

Thanks.
0
Chris
Top achievements
Rank 1
answered on 22 Sep 2011, 02:35 AM
On a second thought, when user already defined a property, OA shouldn't generate the property at all. (or user can choose to generate or not generate properties)
However, if you simply delete OA generated property, "OA config" will complain that it's not present.
With proper care, I think it's the best solution to support hierarchy.
 
Thanks.

0
Ivailo
Telerik team
answered on 23 Sep 2011, 10:04 AM
Hello Chris ,

I think your ideas are very interesting and useful. For that reason I have raised them as feature requests internally and they might be planned for implementation in one of our future releases.

If you feel that you need more flexibility in defining you relational mapping than the Visual Designer is currently offering, I can recommend you another approach. For maximum flexibility, we have implemented the Fluent Mapping API, which allows you to define your relational mapping in a declarative manner in your code. This way you can create any kind of model and since it is customized in your code, it will no be replaced by any code generation. In the same time, you will be able to take advantage of the same OpenAccess ORM persistence and caching features that are available with the Visual Designer.

If you like the idea, you can get started by refering to the overview of Fluent Mapping API. For a detailed explanation of defining horizontal inheritance, you can check this article. If you want to create a sample project to try this API and you have already upgraded to our latest internal build you can make use of our new project templates. When creating the project just go to the Windows category of project templates, make sure you have selected .NET Framework 4.0 and you will see the template Telerik OpenAccess Fluent Library.

In any case, do not hesitate to contact us if you have any further questions.

All the best,
Ivailo
the Telerik team

Thank you for being the most amazing .NET community! Your unfailing support is what helps us charge forward! We'd appreciate your vote for Telerik in this year's SQL Server Community Awards. We are competing in TWO categories and every vote counts! VOTE for Telerik NOW >>

0
Chris
Top achievements
Rank 1
answered on 26 Sep 2011, 08:33 AM
Hi,
I was looking for a solution that allows round-trip model but if I have to I'll take a look at FluentModel API.
I suppose it's code-first approach and it seems more involved to get started than DB first approach.

Before I indulge myself, please let me ask few questions.

1. What are the advantages of using Fluent API over Entity Framework? regarding just core Fluent API without the extra tools and libraries that are not directly related to Fluent API. I know that there are list of advantages of OA but not much said about advantage of Fluent API itself.

2. Getting changes from DB will still work? or I'll have to work through the code and apply changes to DB? The reason why I ask is that someone other than coder can change DB schema and I wonder if it's allowed.

Thanks for your support.

 
0
Serge
Telerik team
answered on 26 Sep 2011, 05:20 PM
Hello Chris ,

Compared to EntityFramework's Code First approach the Fluent Mapping API excels in capabilities. More than 90% of Telerik OpenAccess ORM's functionality is exposed trough this API. This includes thing like artificial (dynamic) fields and types, advanced mappings such as dictionaries, mapping fields with no properties, index mapping and more. 

The Fluent Mapping API is just another mapping tools for OpenAccess so all of the things that make OpenAccess great are still here (such as for example the Level 2 Cache). While creating this API our main goal was to provide a clean and intuitive API for developers that are not keen on visual tools and prefer to have control over everything. 

Unfortunately there is no way to reverse map the mapping from the database when using the Fluent Mapping API, after all the mapping is just custom code that you have written. In order to accommodate changes to the database you will either have to use the fluent mapping and OpenAccess to update the database or after you have updated the database include the necessary mapping in the mapping definition. 

I hope this is helpful.

Greetings,
Serge
the Telerik team

Thank you for being the most amazing .NET community! Your unfailing support is what helps us charge forward! We'd appreciate your vote for Telerik in this year's SQL Server Community Awards. We are competing in TWO categories and every vote counts! VOTE for Telerik NOW >>

0
Chris
Top achievements
Rank 1
answered on 27 Sep 2011, 05:19 AM
Hi,
Thanks for the explanation.
Looking at the API documentation, I was little overwhelmed amount of code I have to type.
Most of them seem to related to plumbing and not very interesting.
However, Fluent Mapping setup is somewhat similar to code generated by Model.
Even though round-trip won't work, I was wondering why you can initally setup Fluent Mapping from DB and use it as the bases.
Right now I'm trying to massage Model generated code to see if I can turn it into Fluent Mapping code.

Thanks.
0
Serge
Telerik team
answered on 27 Sep 2011, 12:32 PM
Hello Chris ,

 We are working on an option for the Visual Designer that would generate Fluent Mapping in your source files, though it will most probably be available with the Q3 release of Telerik OpenAccess ORM. 

What you can easily do though is use the classes generated by the designer and only provide a FluentMetadataSource that contains the mapping code, I believe this is what you are already trying to do.

Also if you want to use the same context that is generated by the rlinq, you will have to change the XmlMetadataSource that it currently used to the new FluentMetadataSource that you are working on.

I hope this helps, please do not hesitate to let us know should you face further difficulties, though I would suggest opening separate support tickets so that we do not pollute this thread.

Kind regards,
Serge
the Telerik team

Thank you for being the most amazing .NET community! Your unfailing support is what helps us charge forward! We'd appreciate your vote for Telerik in this year's SQL Server Community Awards. We are competing in TWO categories and every vote counts! VOTE for Telerik NOW >>

0
Chris
Top achievements
Rank 1
answered on 27 Sep 2011, 01:21 PM
Hmm... I think Fluent Mapping might work for small project but it's too much work involved for my project.
Well.. I'm giving up.. instead, I'm going back Model approach. Hopefully, I'll get it working.
Thanks for all your help and I'll create a support ticket if necessary.
 
Tags
General Discussions
Asked by
Chris
Top achievements
Rank 1
Answers by
Kevin
Top achievements
Rank 1
Chris
Top achievements
Rank 1
Ivailo
Telerik team
Serge
Telerik team
Share this question
or