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
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.
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?
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
I'll give option 1 a try and stop by if there are other problems I find.
Cheers!
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 >>
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.
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.
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 >>
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.
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.
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 >>
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.
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.
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 >>
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.