Sitecore Glass data model inheritence - sitecore

I am using the Glass Mapper on a Sitecore instance where I have a basic data template structure of
Base
BaseWithList
BaseWithExtraContent
BaseWithExtraContentAndCallToActionLink
I have added model classes in my project to follow this structure too. My class names match my template names.
[SitecoreType(TemplateId = "{5D19BD92-799E-4DC1-9A4E-1DDE3AD68DAD}", AutoMap = true)]
public class Base
{
public virtual string Title {get;set;}
public virtual string Content {get;set;}
}
[SitecoreType(TemplateId = "{0491E3D6-EBAA-4E21-B255-80F0607B176D}", AutoMap = true)]
public class BaseWithExtraContent : Base
{
public virtual string ExtraContent {get;set;}
}
[SitecoreType(TemplateId = "{95563412-7A08-46A3-98CB-ABC4796D57D4}", AutoMap = true)]
public class BaseWithExtraContentAndCallToActionLink : BaseWithExtraContent
{
public virtual string CallToActionLink {get;set;}
}
These data models are used from another class that has a list of base type, I want to be able to store any derived type in here so I added attributes as detailed in this tutorial
[SitecoreType(AutoMap = true)]
public class HomePage
{
[SitecoreChildren(InferType = true)]
[SitecoreField(FieldName = "Widgets")]
public virtual IEnumerable<Base> Widgets { get; set; }
}
According to the tutorial this should work. However the list of widget just contains class of the base type.
I then found a later tutorial that said that if you have separated out the models to a different assemblies than the one Glass is installed in you have to add an AttributeConfigurationLoader pointing to the assembly your models are in. The base and derived types are all in the same assembly so I wasn't sure this would solve the issue, but I tried it anyway.
My custom loader config looks like this:
public static class GlassMapperScCustom
{
public static void CastleConfig(IWindsorContainer container)
{
var config = new Config {UseWindsorContructor = true};
container.Install(new SitecoreInstaller(config));
}
public static IConfigurationLoader[] GlassLoaders()
{
var attributes = new AttributeConfigurationLoader("Project.Data");
return new IConfigurationLoader[] {attributes};
}
public static void PostLoad(){
//Remove the comments to activate CodeFist
/* CODE FIRST START
var dbs = Sitecore.Configuration.Factory.GetDatabases();
foreach (var db in dbs)
{
var provider = db.GetDataProviders().FirstOrDefault(x => x is GlassDataProvider) as GlassDataProvider;
if (provider != null)
{
using (new SecurityDisabler())
{
provider.Initialise(db);
}
}
}
* CODE FIRST END
*/
}
}
Upon doing the custom loader config I now get an "Ambiguous match found" exception. I have checked to see if there are any other non Glass attributes set in the classes in that assembly and there aren't.
Any ideas? I guess there are 2 questions.
Why does using the inferred type attribute not load the correct types and only the base types?
Why when I attempt to solve this by adding a custom attribute loader do I get the exception?

Widgets property has two attributes - it's either mapped to the children elements of the item, or a field, can't be both.

Related

Designing Model with foreign key

I am building an ORM by using Unit or Work and Repository using Dapper. I have searched the internet on this problem and no luck.
I have the following tables:
As you can see, Instance has Entity inside. I have 2 approaches:
Approach 1:
public class Entity
{
public int Id {get;set;}
public string Name {get;set;}
}
public class Instance
{
public int Id {get;set;}
public Entity Entity {get;set;}
public string Name {get;set;}
}
How can I get value for Entity with this approach?
Approach 2 (according to this link):
public class Entity
{
public int Id {get;set;}
public string Name {get;set;}
}
public class Instance
{
public int Id {get;set;}
public int EntityId {get;set;}
public string Name {get;set;}
}
Which design is better for use?
You can use QueryMultiple if you want to fetch the data from two different tables and fill it up in two different POCO classes. Following is copied from here:
string sql = "SELECT * FROM Invoice WHERE InvoiceID = #InvoiceID; SELECT * FROM InvoiceItem WHERE InvoiceID = #InvoiceID;";
using (var connection = My.ConnectionFactory())
{
connection.Open();
using (var multi = connection.QueryMultiple(sql, new {InvoiceID = 1}))
{
var invoice = multi.Read<Invoice>().First();
var invoiceItems = multi.Read<InvoiceItem>().ToList();
}
}
Both the models you mentioned in your code can be handled with this approach.
As an alternative approach, you can combine your two POCOs in one or you can use inheritance as well. But, looking at your data model, I do not think this is applicable to this particular case.
Which design is better for use?
Up to you. Whatever suits your project needs keeping down the unnecessary complexities is good for you.

GlassMapper SC nullable boolean fields causing Failed item resolve

We have recently upgraded from Glass.Sitecore.Mapper 2.0.12.0 to Glass.Mapper.Sc 3.3.1.53 (we can't upgrade any higher as we are using Sitecore 6).
We are having problem with saving items with nullable boolean fields
i.e.
[SitecoreType(TemplateId = Templates.Category)]
public class Category : AggregateBase
{
...
[SitecoreField(FieldName = CategoryFields.Requires360)]
public virtual bool? Requires360 { get; set; }
[SitecoreField(FieldName = CategoryFields.RequiresCatwalk)]
public virtual bool? RequiresCatwalk { get; set; }
}
Where AggregateBase contains the standard sitecore fields
e.g.
[SitecoreType]
public class AggregateBase
{
[SitecoreId]
public virtual Guid Id { get; set; }
[SitecoreInfo(SitecoreInfoType.Name)]
public virtual string Name { get; set; }
...
}
If the nullable bool field is null it saves without issue.
If the nullable field has a value we get the following exception
"Failed item resolve - You cannot save a class that does not contain a
property that represents the item ID. Ensure that at least one
property has been marked to contain the Sitecore ID. Type:
System.Boolean"
This configuration worked previously and seems to work with other nullable types e.g. int?
The underlying storage in the template field is "Single-Line Text".
Does anyone have any ideas?
Regards,
Mark

EF6 Code First: How can I centralize content for separate sections of my site?

I want to store all our site's content in one central Content table but relate it to each section of the site. Something like:
Content (for the actual content byte[] and basic info all sections use)
ResearchArticleContent (basically has the related ContentId from the content table and extra cols for info specific to ResearchArticles)
ResearchArticle
ExecutiveContent (basically has the related ContentID from Content table and extra cols for specific data for Executives)
Executive
...and so on.
I'm having trouble understanding the whole code first approach as it pertains to ForeignKeys and InverseProperties. That's the real issue.
So, say I have these two classes as an example:
public class Content
{
[Key]
public int ContentId { get; set; }
public int ContentType { get; set; }
public byte[] ContentBytes { get; set; }
public DateTime AddedDate { get; set; }
[**`InverseProperty or ForeignKey???`**("ResearchArticleContent")]
public virtual ResearchArticleContent ResearchArticleContent { get; set; }
}
and:
public class ResearchArticleContent
{
[Key]
public int ResearchArticleContentId { get; set; }
[ForeignKey("ContentId")]
public virtual Content Content {get;set;}
public int ResearchArticleId { get; set; }
[ForeignKey("ResearchArticleId")]
public virtual ResearchArticle RelatedArticle { get; set; }
}
Where do I put the ForeignKeys / InverseProperties to relate these correctly. Because ideally, I will have Executivecontent, ResearchArticlecontent and so on for each section of the site. (I am following the precedent already laid out in a Data-First prj that I am mimicking so this is the way I have do this, fyi.)
Entity framework requires a type identifier field when you store compound objects in a single table; however, you can get around this pretty easily using views. To use views, create a single content table and a > base < class. Do not apply the TableAttribute data annotation to the base class. All other data annotations are fine.
public class ContentBase
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ContentId { get; set; }
public string Content { get; set; }
...
}
Then, you can create derived classes that more closely represent the content and apply the TableAttribute data annotation to those. For example,
[Table("ResearchArticleView")]
public class ResearchArticle : ContentBase
{
...you can add more properties here that are included in the view...
...and not necessarily the underlying table, like from a joined table...
...or just use the class as is, so that you have a better name...
}
To use this, set up a view called ResearchArticleView that includes the columns in the base class, as well as any computed or joined columns you want, then add a DbSet to your context that represents the view.
I recommend having content tables for each type of content and then use the method I've described for derived types for each content type. For example, create a base for research articles and a base for execute content. Because, when your database gets big and full of content, having one monolithic content table may cause you backup and optimization issues.

Why is AutoFixture Customization causing inherited properties to not be filled?

I wrote the following customization and have it applied as part of a composite on most of my tests. My entities have a read-only Id, but I'm using their SetId method in this customization to make sure all entities have some Id if they are transient (don't have an Id already).
public class SetEntityIdCustomization : ICustomization {
public void Customize(IFixture fixture) {
var engine = ((Fixture)fixture).Engine;
fixture.Customizations.Add(new Postprocessor(
engine, o => {
var entity = o as BaseEntity;
if (entity == null || !entity.IsTransient()) {
return;
}
entity.SetId(fixture.CreateAnonymous<Guid>());
}));
}
}
This has been working great, until I discovered a very odd thing today. If I feed a test one of my entities that directly inherits from BaseEntity, all is well and it's writeable properties are auto-filled. However, if I ask for an entity that inherits from something further down from BaseEntity, my customization prevents the properties from auto-filling.
The User entity in this test method is filled properly:
public class User : BaseEntity {
public string Email { get; set; }
public int CoolThings { get; set; }
}
...
[Theory, AutoDomainData]
public void SomeTest(User user, ...) {
// user.Email and user.CoolThings have auto-filled values, as expected.
...
}
However, the AwesomeUser entity in the following test does not get any of the same properties auto-filled.
public class AwesomeUser : User {
...
}
...
[Theory, AutoDomainData]
public void SomeOtherTest(AwesomeUser user, ...) {
// user.Email nor user.CoolThings have auto-filled values. What gives?
...
}
In both test cases, the Id property is auto-filled because of my customization. If I remove my customization, the SomeOtherTest's AwesomeUser instance gets its inherited properties auto-filled just fine. I must assume that my customization is what is messing things up.
Is there a better way to get all my BaseEntity instances to set their Id, or is there something else I'm missing with AutoFixture? I've applied my customization first, in the middle, and last, to no avail.
The solution provided above is a pretty clever attempt, but not something I've seen before. A more idiomatic solution would be something like this:
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(
new FilteringSpecimenBuilder(
new Postprocessor(
new BaseEntityBuilder(
new ConstructorInvoker(
new ModestConstructorQuery())),
new AutoPropertiesCommand().Execute),
new BaseEntitySpecification()));
}
private class BaseEntityBuilder : ISpecimenBuilder
{
private readonly ISpecimenBuilder builder;
private readonly IRequestSpecification specification;
public BaseEntityBuilder(ISpecimenBuilder builder)
{
this.builder = builder;
this.specification = new BaseEntitySpecification();
}
public object Create(object request, ISpecimenContext context)
{
if (!this.specification.IsSatisfiedBy(request))
return new NoSpecimen(request);
var b = (BaseEntity)this.builder.Create(request, context);
b.SetId((Guid)context.Resolve(typeof(Guid)));
return b;
}
}
private class BaseEntitySpecification : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
var t = request as Type;
if (t == null)
return false;
if (!typeof(BaseEntity).IsAssignableFrom(t))
return false;
return true;
}
}
As you can see, this isn't a simple one-liner, which is indicative of AutoFixture being a rather opinionated library. In this case, AutoFixture's opinion is:
Favor object composition over class inheritance.
-Design Patterns, p. 20
AutoFixture is first and foremost a TDD tool, and one of the main advantages of TDD is that it provides feedback about class design. In this case, the feedback is: Inheritance is awkward and troublesome. Reconsider the design.

Need to Seriazlize List<object>, but FXCop complains "Do not expose generic lists"

I have an object that I need to serialize. The object contains several properties, including a List. FXCop is complaining that I should not expose generic lists, and I get that, however, due to the fact that I can't specify an interface based property on an object that I want serialized I'm not sure where to turn next.
Any thoughts?
BTW, I'm using XMLSerialization, but that's not a requirement.
I took FxCop's suggestion and wrapped my list in a Collection. This blew some of my code out of the water, but a after a few adjustments I was up and running again.
Here's some code showing before and after:
Before:
public class PersistentDataView
{
public string Title { get; set; }
private List<object> Inputs { get; set;}
}
After:
public class PersistentDataView
{
private List<object> _inputs;
public string Title { get; set; }
public Collection<object> Inputs
{
get
{
if (_inputs == null)
_inputs = new List<object>();
//Wrap the private field into a collection.
return new Collection<object>(_inputs);
}
}
}