Workable Dynamic Type sample?? Error: No metadata has been registered for class Telerik.Sitefinity.DynamicTypes.Model.MyClass
I cannot get a simple dynamic type sample working. Any error with it permanently crashes the Sitefinity site, even with deleting rows from the sf_meta_types table. I am constantly having to restore a complete database to start again.
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below. |
|
using
System;
using
System.Linq;
using
System.Web.UI;
using
Telerik.Sitefinity;
using
Telerik.Sitefinity.Data.Metadata;
using
Telerik.Sitefinity.DynamicTypes.Model;
using
Telerik.Sitefinity.Metadata.Model;
using
Telerik.Sitefinity.Model;
namespace
SitefinityWebApp.Controls
public
partial
class
DynSample : System.Web.UI.UserControl
protected
void
Page_Load(
object
sender, EventArgs e)
var metaMan = MetadataManager.GetManager();
var dynType = metaMan.GetMetaType(
typeof
(MyClass));
//CHECK IF TYPE EXISTS BEFORE CREATION
if
(dynType ==
null
)
dynType = metaMan.CreateMetaType(
typeof
(MyClass));
App.WorkWith()
.DynamicData()
.Type()
.CreateNew(dynType.GetType())
.Do(dt => dt.DatabaseInheritance = DatabaseInheritanceType.vertical)
.Field()
.CreateNew(
"UserName"
,
typeof
(
string
))
.Done()
.Field()
.CreateNew(
"UserDate"
,
typeof
(DateTime))
.Done()
.SaveChanges(
true
);
//UPDATE USER DATE FOR USER
if
(
false
)
App.WorkWith()
.DynamicData()
.Type(
typeof
(MyClass))
.Get()
.Fields
.Where(f => f.GetValue<
string
>(
"UserName"
).Equals(
"admin"
, StringComparison.OrdinalIgnoreCase))
.SingleOrDefault()
.SetValue(
"UserDate"
, DateTime.Now);
//GET RESULTS
var userStuff = App.WorkWith()
.DynamicData()
.Type(
typeof
(MyClass))
.Get()
.Fields
.Where(f => f.GetValue<
string
>(
"UserName"
).Equals(
"admin"
, StringComparison.OrdinalIgnoreCase))
.SingleOrDefault();
//DISPLAY RESULTS
this
.Controls.Add(
new
LiteralControl(String.Format(
"User Name: 0<br />User Date: 1"
,
userStuff.GetString(
"UserName"
), userStuff.GetValue<DateTime>(
"UserDate"
))));
namespace
Telerik.Sitefinity.DynamicTypes.Model
public
class
MyClass
public
string
UserName
get
;
set
;
public
DateTime UserDate
get
;
set
;
public
MyClass()
this
.UserDate = DateTime.Now;
Hi bemara57,
I am sorry to disappoint you, but you wouldn't be able to do artificial types in assemblies outside of Telerik.Sitefinity.Model.dll. It is a problem that we have just recently addressed.
We are currently putting effort into removing issues like this one - issues that compromise Sitefinity's extensibility. This, and many others, were recently solved, and will be included in the next pre-official release (planned for the next week). Along with that, we are working on a sample custom module (the well-known ProductsModule from 3.x) that could serve as a starting point for module developers. It should come with a very short documentation that briefly covers what-where-why.
Thanks for the response. Now that RC2 has been released for a few days, any chance we can get a sneak peek to dynamic types in this thread? I actually got a little further implementing my sample below on an RC2 build, but get this new error:
"Cannot "SetValue", because does not contain a public property named "UserDate"."
I was hoping my example above can be adjusted to work, or some guidance on any updated SF4 samples/docs for dynamic types. Thanks.
public
partial
class
SampleDyn : System.Web.UI.UserControl
protected
void
Page_Load(
object
sender, EventArgs e)
var metaMan = MetadataManager.GetManager();
var dynType = metaMan.GetMetaType(
typeof
(MyClass));
//CHECK IF TYPE EXISTS BEFORE CREATION
if
(dynType ==
null
)
dynType = metaMan.CreateMetaType(
typeof
(MyClass));
App.WorkWith()
.DynamicData()
.Type()
.CreateNew(dynType.GetType())
.Do(dt => dt.DatabaseInheritance = DatabaseInheritanceType.vertical)
.Field()
.CreateNew(
"UserName"
,
typeof
(
string
))
.Done()
.Field()
.CreateNew(
"UserDate"
,
typeof
(DateTime))
.Done()
.SaveChanges(
true
);
//UPDATE USER DATE FOR USER
var userStuff = App.WorkWith()
.DynamicData()
.Type(
typeof
(MyClass))
.Get()
.Fields
.Where(f => f.GetValue<
string
>(
"UserName"
).Equals(
"admin"
, StringComparison.OrdinalIgnoreCase))
.SingleOrDefault();
if
(userStuff !=
null
)
userStuff.SetValue(
"UserName"
,
"admin"
);
userStuff.SetValue(
"UserDate"
, DateTime.Now);
//DISPLAY RESULTS
this
.Controls.Add(
new
LiteralControl(String.Format(
"User Name: 0<br />User Date: 1"
,
userStuff.GetString(
"UserName"
), userStuff.GetValue<DateTime>(
"UserDate"
))));
namespace
Telerik.Sitefinity.DynamicTypes.Model
public
class
MyClass
public
string
UserName
get
;
set
;
public
DateTime UserDate
get
;
set
;
public
MyClass()
this
.UserDate = DateTime.Now;
Hi bemara57,
1. You should add the following attribute TypeDescriptionProvider ato your custom type ( class) and you do not need the public properties in it.
MyClass should inherit from IDynamicFiledsContainer.
namespace
Telerik.Sitefinity.DynamicTypes.Test.Model
[TypeDescriptionProvider(
typeof
(DynamicFieldsTypeDescriptionProvider))]
public
class
MyClass : IDynamicFieldsContainer
public
Guid ID
get
;
set
;
public
MyClass()
var metaMan = MetadataManager.GetManager();
var dynType = metaMan.GetMetaType(
typeof
(MyClass));
if
(dynType !=
null
)
metaMan.Delete(dynType);
metaMan.SaveChanges(
true
);
metaMan = MetadataManager.GetManager();
dynType = metaMan.CreateMetaType(
typeof
(MyClass));
var metaField = metaMan.CreateMetafield(
"UserName"
);
metaField.DBSqlType =
"NVARCHAR(MAX)"
;
metaField.DBType =
"LONGVARCHAR"
;
metaField.ClrType =
typeof
(
string
).Name;
dynType.Fields.Add(metaField);
metaMan.SaveChanges(
true
);
var userStuff = App.WorkWith()
.DynamicData()
.Type(
typeof
(MyClass))
.Get()
.Fields
.Where(f => f.GetValue<
string
>(
"UserName"
).Equals(
"admin"
, StringComparison.OrdinalIgnoreCase))
.SingleOrDefault();
Does the dynamic type have to reside in the Telerik.Sitefinity.DynamicTypes.Model namespace?
Hi,
In the code below the namespace is different from this one you are referring to
namespace
Telerik.Sitefinity.DynamicTypes.Test.Model.
I used the code you provided, but end up with this error below. I tried creating the dynamic type in the Init, Load, PreRender, and even after the CreateChildControl events... so I guess I have to do this in a module instead of a widget, otherwise the lifecycle trips up(?). I also tried using the "using" keyword to auto-handle the dispose, but no luck.
Anyways, I checked the database and see the entries for the new dynamic type were created in the sf_meta_fields and sf_meta_types tables... so it got that far. I may be ready to create a custom manager from this point, which seems like a long and tedious road.
Could I inherit ContentManager and/or ContentItem for rapid development? Or, how about creating a DynamicManager that can be used for all dynamically created types (even as a base manager)?
Thanks for the help.
----------------------------------------
Server Error in '/Test2' Application.
The ObjectScope has already been disposed and it's managed persistent objects can no longer be accessed. The ObjectScope should be disposed at the end of the life cycle of your business logic instance. This can also be done in the Dispose of your ASP page or MVC controller.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Object name: 'Telerik.OpenAccess.RT.ObjectScope'.
Exception Details: System.ObjectDisposedException: The ObjectScope has already been disposed and it's managed persistent objects can no longer be accessed. The ObjectScope should be disposed at the end of the life cycle of your business logic instance. This can also be done in the Dispose of your ASP page or MVC controller.
Object name: 'Telerik.OpenAccess.RT.ObjectScope'.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[ObjectDisposedException: The ObjectScope has already been disposed and it's managed persistent objects can no longer be accessed. The ObjectScope should be disposed at the end of the life cycle of your business logic instance. This can also be done in the Dispose of your ASP page or MVC controller.
Object name: 'Telerik.OpenAccess.RT.ObjectScope'.]
DynamicModule.ns.Wrapped_OpenAccessPageProvider_b40c2b53d88c48829647a63560bf5f35.GetPageNode(Guid id) +413
Telerik.Sitefinity.Modules.Pages.PageManager.GetPageNode(Guid id) +57
Telerik.Sitefinity.Web.UI.PublicControls.BrowseAndEdit.BrowseAndEditPageMenu.InitializeControls(GenericContainer container) +145
Telerik.Sitefinity.Web.UI.SimpleView.CreateChildControls() +82
System.Web.UI.Control.EnsureChildControls() +182
System.Web.UI.Control.PreRenderRecursiveInternal() +73
System.Web.UI.Control.PreRenderRecursiveInternal() +240
System.Web.UI.Control.PreRenderRecursiveInternal() +240
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3914
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.1
-----------------------------------------------
using
System;
using
System.ComponentModel;
using
Telerik.Sitefinity.Data.Metadata;
using
Telerik.Sitefinity.Model;
namespace
SitefinityWebApp.Controls
public
partial
class
DynamicSample : System.Web.UI.UserControl
protected
override
void
OnInit(EventArgs e)
base
.OnInit(e);
this
.Install();
protected
void
Page_Load(
object
sender, EventArgs e)
public
void
Install()
bool
restart =
false
;
this
.EnsureDynamicTypes(
ref
restart);
//WILL THIS BE NEEDED??
//if (restart)
//
// SystemManager.RestartApplication(false);
//
private
void
EnsureDynamicTypes(
ref
bool
restart)
using
(var metaMan = MetadataManager.GetManager())
var dynType = metaMan.GetMetaType(
typeof
(MyClass));
//CHECK IF DYNAMIC TYPE EXISTS ALREADY
if
(dynType ==
null
)
//CREATE DYNAMIC TYPE
dynType = metaMan.CreateMetaType(
typeof
(MyClass));
//ADD META FIELDS
var metaField = metaMan.CreateMetafield(
"CustomText"
);
metaField.DBSqlType =
"NVARCHAR(MAX)"
;
metaField.DBType =
"LONGVARCHAR"
;
metaField.ClrType =
typeof
(
string
).Name;
dynType.Fields.Add(metaField);
//PERSIST TO DB
metaMan.SaveChanges(
true
);
else
//DO NOTHING OR HANDLE BELOW
return
;
//HANDLE IF TYPE CREATED BEFORE
metaMan.Delete(dynType);
metaMan.SaveChanges(
true
);
Response.Write(
"Deleted existing type. Refresh for creating again."
);
[TypeDescriptionProvider(
typeof
(DynamicFieldsTypeDescriptionProvider))]
public
class
MyClass : IDynamicFieldsContainer
public
Guid ID
get
;
set
;
public
MyClass()
Hi bemara57,
The Sitefinity API does not currently allow you to manage dynamic types without having a manager. We are planning to include a new manager that will automate this and make it easier for you to create new dynamic types and work with them, thus allowing RAD with simple persistent types.
The code below is a temporary workaround until that API is ready. I have placed comments in the places I felt a clarification was necessary. Please, use this code with care, or at least wrap it in a helper class, so that you will be able to use the new API once it is done.
const
string
DynTypeNamespace =
"Telerik.Sitefinity.Support.Samples"
;
const
string
DynTypeClassname =
"MyDynType"
;
const
string
FieldTitle =
"Title"
;
const
string
FieldContent =
"Content"
;
Guid ItemID =
new
Guid(
"55094178-8E87-4B92-8495-CCDC49357AAA"
);
var metaManager = MetadataManager.GetManager();
var metaType = metaManager.GetMetaType(DynTypeNamespace, DynTypeClassname);
if
(metaType ==
null
)
// this is how you create a dynamic type (i.e. creating a new table in the database)
metaType = metaManager.CreateMetaType(DynTypeNamespace, DynTypeClassname);
// This is what tells Sitefinity that this is a dynamic type
metaType.IsDynamic =
true
;
metaType.DatabaseInheritance = Telerik.Sitefinity.Metadata.Model.DatabaseInheritanceType.vertical;
// DynamicTypeBase is used internally and is guaranteed to work; do not create your own base types
metaType.BaseClassName =
typeof
(Telerik.Sitefinity.DynamicTypes.Model.DynamicTypeBase).FullName;
// creating a few fields
var titleField = metaManager.CreateMetafield(FieldTitle);
titleField.ClrType =
typeof
(
string
).FullName;
titleField.DBSqlType =
"NVARCHAR"
;
metaType.Fields.Add(titleField);
var contentField = metaManager.CreateMetafield(FieldContent);
contentField.ClrType =
typeof
(
string
).FullName;
contentField.DBSqlType =
"NVARCHAR(MAX)"
;
contentField.DBType =
"LONGVARCHAR"
;
metaType.Fields.Add(contentField);
// You HAVE TO restart the web site
// You are making changes to the database model itself (creating new tables)
// so the OpenAccess connection has to be restarted, as well as anything Sitefinity caches
metaManager.SaveChanges(
true
);
SystemManager.RestartApplication(
false
);
metaManager =
null
;
// after a restart, you have to get all of your managers anew. If the app weren't restarted, this would return the
// same instance of the manager/provider, so no harm done
metaManager = MetadataManager.GetManager();
// All persistent types that are going to be accessed should be got anew
metaType = metaManager.GetMetaType(DynTypeNamespace, DynTypeClassname);
// You have to use TypeResolutionService, because OpenAccess builds a new assembly in memory
// for dynamic types, which may not be loaded in your AppDomain. TypeResolutionService
// takes care of this.
var clrType = TypeResolutionService.ResolveType(
string
.Concat(DynTypeNamespace,
"."
, DynTypeClassname));
// Workaround to retrieve IObjectScope -> do not use in production code
// effectively "stealing" the object scope of the metadata manager. this is just for demonstration.
// do not use => create your own manager instead,
// or retrieve scope to your database in some other way
IObjectScope scope = OpenAccessExtensions.GetScope((IOpenAccessDataProvider)metaManager.Provider);
var descriptor = scope.PersistentMetaData.GetPersistentTypeDescriptor(clrType);
// you should have using Telerik.Sitefinity.Data.Linq.Dynamic;
// Workaround to retrieve IQueryable (not generic) for a persistent type without having your own manager/provider
// This is how you retrieve item type when you are not sure it exists
var item =
SitefinityQuery
.Get(descriptor.DescribedType, metaManager.Provider)
// You should always use Dynamic Linq for dynamic types!
.Where(
"Id = @0"
, ItemID)
.FirstOrDefault()
as
Telerik.Sitefinity.DynamicTypes.Model.DynamicTypeBase;
if
(item !=
null
)
// this is how you can retrieve the item when you are sure it exits (e.g. you got the ID from a Grid)
// this is a lot faster, as it uses OpenAccess' cache
object
instance = scope.GetObjectById(Telerik.OpenAccess.Database.OID.ParseObjectId(clrType, ItemID.ToString()));
item = (Telerik.Sitefinity.DynamicTypes.Model.DynamicTypeBase)instance;
else
// this is how you create an instance
item = (Telerik.Sitefinity.DynamicTypes.Model.DynamicTypeBase)descriptor.CreateInstance(ItemID);
// to be able to save changes, you have to add it to the scope
scope.Add(item);
// retrieving a value
PropertyDescriptor titleDesc = TypeDescriptor.GetProperties(item)[FieldTitle];
string
title = (
string
)titleDesc.GetValue(item);
if
(title !=
"Hello, dynamic types"
)
// setting a value
titleDesc.SetValue(item,
"Hello, dynamic types"
);
// when you are done, simply commit the transaction
metaManager.SaveChanges();
I just wanted to follow up on this thread to see if their was an ETA on the Dynamic Type Manager. To me that has the potential to be one of the most powerful features of Sitefinity or any CMS on the market, but in its current state it is a bit of a tease.
Hi Joseph,
We have planned improvements for our API (fluent and native) during Q2 and those improvements will be made part of the Sitefinity 4.2 release. We will try to expose better API for Dynamic Types as part of those improvements.
Kind regards,
Radoslav Georgiev
the Telerik team
Yay! Sitefinity 4.2 is here. (You still need to add the word Sitefinity to your spell check)
I wanted to follow up on this thread and ask if if you can point me in the direction of where this API lives and how I may use it to instantiate a dynamic type.
Much Thanks!
Hello Joseph,
Unfortunately we did not get a chance to improve the dynamic type API apart from what Dido sampled in this forum thread. It is still on our to do list and we will soon update the the Sitefinity road map when it makes into the plan.
Best wishes,
Radoslav Georgiev
the Telerik team
Hello Radoslav,
I was wondering If Telerik has made any progress concerning the dynamic types API. I want for example programmatically add (producttype) books to my product list (import them from another source). I can add products with your API, but I cannot set any (dynamic) productttype fields, for example the isbn or number of pages, or am I wrong?
Best regards,
Peter
Hi Peter,
You can set values of custom fields using SetValue() extension method. To be able to use this method you have to reference Telerik.Sitefinity.Model namespace.
We are making progress on the dynamic type API and those improvements will be released with Sitefinity 4.4 as part of the module builder tool.
Kind regards,
Radoslav Georgiev
the Telerik team
Hello Radoslav,
I understand that I can us SetValue() for dynamic fields (did that before), but those are dynamic fields of existing sitefinity objects (not dynamic types). In the telerik Ecommerce module you have a dynamic type book with for example dynamic field "number of pages". How can I programmatically set those values?
Good to hear you are making progress. Looking forward to 4.4 :)!
Regards,
Peter
Hi Peter,
Book is a product type - ProductType. The product type implements two interfaces IDataItem and IDynamicFieldsContainer which are responsible for the persistent types.
NumberOfPages is a metakey of book, so you should be able to call GetValue or SetValue extensions methods.
ProductType book = catalogManager.GetProductTypes().Where(pt => pt.Title == "Book").SingleOrDefault();
book.SetValue("NumberOfPages", 1);
Kind regards,
Ivan Dimitrov
the Telerik team
Hi Ivan,
You are right :). Didn't think it was that easy with dynamic types and fields. Your example however is not totally correct, you use the ProductType object, but you need to use the Product object, for example:
Telerik.Sitefinity.Modules.Ecommerce.Catalog.CatalogManager cm = new Telerik.Sitefinity.Modules.Ecommerce.Catalog.CatalogManager();
Product p = cm.GetProducts().Where(pr => pr.Title == "Test Book").SingleOrDefault();
p.SetValue("NumberOfPages", 123);
cm.SaveChanges();
Product p = cm.GetProducts().Where(pr => pr.GetValue<
Product
>("ISBN").ToString() == "123123123").SingleOrDefault();
Hi, now there's a 4.4 is the API available to now create/update these dynamic types? Or is the workaround given by Dido still necessary?
Thanks
Tim
Hello,
We have module builder which generates code samples so you can observe it to see how to create dynamic types.You should use GetValue/SetValue to get/update the dynamic type.
All the best,
Ivan Dimitrov
the Telerik team