Closed Nicholas-Westby closed 5 years ago
Looks like the problem is on this line:
The portion that says entity.GetPropertyValue(collection.IdProperty)
returns null, which makes sense, since this is a new entity and a new entity can have a null ID.
The .Equals(
is then called on this null instance. Since .Equals
is not an extension method, I imagine this is why it's bombing out (because you can't call methods on null instances).
My guess is that this is not usually an issue because you typically work with non-nullable value types (e.g., integer) for your ID types. I'm using a string as my ID in this case. So I imagine the fix is to ensure this works with nullable types by accounting for the possibility that the ID may be null.
Until the pull request gets merged, I created a weird workaround. I'll share it here for others in the same predicament.
The model class:
public class SamplePerson
{
public SampleStringIdentifier Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Repo class:
using Fluidity;
using Fluidity.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Umbraco.Core.Models;
public class SampleRepo : FluidityRepository<SamplePerson, SampleStringIdentifier>
{
private static List<SamplePerson> People = new List<SamplePerson>()
{
new SamplePerson()
{
Id = new SampleStringIdentifier("Bob"),
FirstName = "Bob",
LastName = "Boberson"
},
new SamplePerson()
{
Id = new SampleStringIdentifier("Jane"),
FirstName = "Jane",
LastName = "Janerson"
}
};
protected override void DeleteImpl(SampleStringIdentifier id)
{
People = People.Where(x => x.Id.Id != id.Id).ToList();
}
protected override IEnumerable<SamplePerson> GetAllImpl()
{
return People;
}
protected override SampleStringIdentifier GetIdImpl(SamplePerson entity)
{
return entity.Id;
}
protected override SamplePerson GetImpl(SampleStringIdentifier id)
{
return People.Where(x => x.Id.Id == id.Id).FirstOrDefault();
}
protected override PagedResult<SamplePerson> GetPagedImpl(int pageNumber, int pageSize,
Expression<Func<SamplePerson, bool>> whereClause, Expression<Func<SamplePerson, object>> orderBy,
SortDirection orderDirection)
{
var pageIndex = pageNumber - 1;
var compiledWhere = whereClause == null
? new Func<SamplePerson, bool>(x => true)
: whereClause.Compile();
var compiledOrder = orderBy == null
? new Func<SamplePerson, object>(x => x.FirstName)
: orderBy.Compile();
var result = People
.Where(x => compiledWhere(x))
.OrderBy(x => compiledOrder(x))
.Skip(pageIndex * pageSize)
.Take(pageSize)
.ToList();
return new PagedResult<SamplePerson>(People.Count, pageNumber, pageSize)
{
Items = result
};
}
protected override long GetTotalRecordCountImpl()
{
return People.Count;
}
protected override SamplePerson SaveImpl(SamplePerson entity)
{
if (string.IsNullOrWhiteSpace(entity.Id.Id))
{
entity.Id = new SampleStringIdentifier(Guid.NewGuid().ToString("N"));
People.Add(entity);
}
else
{
People = People.Select(x => x.Id.Id == entity.Id.Id ? entity : x).ToList();
}
return entity;
}
}
Bootstrap file:
using Fluidity.Configuration;
public class SampleBootstrap : FluidityConfigModule
{
public override void Configure(FluidityConfig config)
{
config.AddSection("Fluidity Sample", sampleConfig =>
{
sampleConfig.SetTree("Sample Tree", treeConfig =>
{
treeConfig.AddCollection<SamplePerson>(x => x.Id, "Person", "People", "A collection of people", collConfig =>
{
collConfig.SetRepositoryType<SampleRepo>();
collConfig.SetNameProperty(y => y.FirstName);
collConfig.SetViewMode(Fluidity.FluidityViewMode.List);
collConfig.ShowOnDashboard();
collConfig.ListView(listView =>
{
//listView.AddField(y => y.FirstName);
listView.AddField(y => y.LastName);
});
collConfig.Editor(editorConfig =>
{
editorConfig.AddTab("About", tab =>
{
//tab.AddField(y => y.FirstName).MakeRequired().SetDescription("The first name of this individual.");
tab.AddField(y => y.LastName).MakeRequired();
});
editorConfig.AddTab("Meta", tab =>
{
tab.AddField(y => y.Id).MakeReadOnly();
});
});
});
});
});
}
}
New identifier struct:
[TypeConverter(typeof(SampleStringIdentifierConverter))]
[Serializable]
public struct SampleStringIdentifier
{
public string Id { get; set; }
public SampleStringIdentifier(string id)
{
Id = id;
}
public override string ToString()
{
return Id ?? string.Empty;
}
}
Type converter for identifier struct:
using System;
using System.ComponentModel;
using System.Globalization;
public class SampleStringIdentifierConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
if (string.IsNullOrWhiteSpace(value as string))
{
return default(SampleStringIdentifier);
}
else
{
return new SampleStringIdentifier(value as string);
}
}
return base.ConvertFrom(context, culture, value);
}
}
Here's what I had to do:
Pretty wonky. Looking forward to the next release.
Should be fixed in 690f30a where I've swapped it to
var isNew = Equals(entity.GetPropertyValue(collection.IdProperty), collection.IdProperty.Type.GetDefaultValue());
Here's the relevant portion of the stack trace from the Umbraco error log file:
Here's my model:
Here's my custom repository:
And here's my bootstrap file:
It mostly works. When I try to save a new entity, it bombs out, like this:
Not really sure what the issue could be. Maybe I've missed some config or maybe it's a Fluidity bug relating to custom repositories?
More info:
SampleRepo
class. None of them are called when I try to save an entity.