How do I Mock a EF Code First Repository given this interface? - entity-framework-4.1

I am doing a project at work and part of my goal is to learn and apply a test-drive development approach.
I am designing a business class that works with a EF code first repository, but I would like to create a mock of the repository rather than actually hit the database.
Given the following repository interface how can I accomplish this with a mocking framework like MOQ?
The challenge is how do I moq the Find method which lets you include other entities?
public interface IRepository<T> where T : EntityBase, new()
{
ICollection<T> Find(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] fetchSelectors);
ICollection<T> Find(Expression<Func<T, bool>> predicate, int pageNumber, int size, Expression<Func<T, object>> orderBy, string sortOrder, out int count, params Expression<Func<T, object>>[] fetchSelectors);
ICollection<T> FindAll(params Expression<Func<T, object>>[] fetchSelectors);
ICollection<T> FindAll(int pageNumber, int size, Expression<Func<T, object>> orderBy, string sortOrder, out int count, params Expression<Func<T, object>>[] fetchSelectors);
void Save(T entity);
void Delete(T entity);
T Create();
}
Here is the implementation of my generic repository:
public class GenericRepository<T> : IRepository<T> where T : EntityBase, new()
{
public GenericRepository(IDbContext context)
{
Guard.ArgumentNotNull(context, "context");
this.Context = context;
this.DbSet = this.Context.CreateDbSet<T>();
}
protected IDbContext Context
{
get;
set;
}
protected System.Data.Entity.IDbSet<T> DbSet
{
get;
set;
}
public virtual ICollection<T> Find(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] fetchSelectors)
{
return this.BuildQuery(predicate, fetchSelectors).ToList();
}
public virtual ICollection<T> FindAll(params Expression<Func<T, object>>[] fetchSelectors)
{
return this.BuildQuery(p => true, fetchSelectors).ToList();
}
public virtual void Save(T entity)
{
Guard.ArgumentNotNull(entity, "entity");
this.Context.SaveOrUpdate(entity);
}
public virtual void Delete(T entity)
{
Guard.ArgumentNotNull(entity, "entity");
this.DbSet.Remove(entity);
}
public T Create()
{
return this.DbSet.Create();
}
private IQueryable<T> BuildQuery(Expression <Func<T, bool>> predicate, params Expression<Func<T, object>>[] fetchSelectors)
{
var query = this.DbSet as IQueryable<T>;
if (fetchSelectors != null)
{
foreach (var fetchSelector in fetchSelectors)
{
query = query.Include(fetchSelector);
}
}
return query.Where(predicate);
}
public ICollection<T> Find(Expression<Func<T, bool>> predicate, int pageNumber, int size, Expression<Func<T, object>> orderBy, string sortOrder, out int count, params Expression<Func<T, object>>[] fetchSelectors)
{
count = (this.DbSet as IQueryable<T>).Count(predicate);
if (size < 1 || size > count)
{
throw new ArgumentOutOfRangeException("size");
}
var maxPageNumber = (count + size - 1) / size;
if (pageNumber < 1 || pageNumber > maxPageNumber)
{
throw new ArgumentOutOfRangeException("pageNumber");
}
if (sortOrder != "asc" && sortOrder != "desc")
{
throw new ArgumentException("sortOrder");
}
var skipCount = (pageNumber - 1) * size;
var query = BuildQuery(predicate, fetchSelectors);
query = sortOrder == "asc" ? query.OrderBy(orderBy) : query.OrderByDescending(orderBy);
return query.Skip(skipCount).Take(size).ToList();
}
public ICollection<T> FindAll(int pageNumber, int size, Expression<Func<T, object>> orderBy, string sortOrder, out int count, params Expression<Func<T, object>>[] fetchSelectors)
{
return Find(p => true, pageNumber, size, orderBy, sortOrder, out count, fetchSelectors);
}
}
Finally, here is IDbContext
public interface IDbContext
{
void SaveOrUpdate<T>(T entity) where T : EntityBase;
IDbSet<TEntity> CreateDbSet<TEntity>() where TEntity: EntityBase;
}

So lets say that you have a business logic class that has a dependency on IRepository<T>:
public class FooBusinessLogicClass
{
readonly IRepository<Foo> repository;
public FooBusinessLogicClass( IRepository<Foo> repository )
{
this.repository = repository;
}
public ICollection<Foo> FindFoo()
{
...
var collection = repository.Find( x => x.SomeLambdaExpression(), y => y.SomeOtherExpression() );
...
return collection;
}
}
If you just want your repository to return some fake data when it is called and you don't care what parameters are passed to it you can use the Moq function It.IsAny<T>():
public void Verify()
{
//Arrange
var repositoryMock = new Mock<IRepository<Foo>>();
var example = new FooBusinessLogicClass( repositoryMock.Object );
//Our fake data for the repository to return
var expectedResult = new[] { new Foo(), new Foo() };
//Our setup that ignores the lambda expressions
repositoryMock.Setup( mock => mock.Find(
It.IsAny<Expression<Func<Foo, bool>>>(),
It.IsAny<Expression<Func<Foo, object>>[]>() ) )
.Returns( expectedResult );
//Act
var actualResult = example.FindFoo();
//Assert
Assert.AreEqual( expectedResult, actualResult );
}
}
If you do care what the parameters are (let's say you want to verify that Find was called), you just have to include the lambda expressions in your Verify():
//Assert
repositoryMock.Verify( mock => mock.Find(
x => x.SomeLambdaExpression(),
y => y.SomeOtherExpression() ) );
In any case, the details of the implementation of GenericRepository<T> and the interface of IDBContext are irrelevant to the testing of our business logic class. Now, you probably want to write unit tests against your implementation of GenericRepository<T> (using a mock of IDBContext), but that is separate concern.

Related

Is it possible to tell Newtonsoft to map a declared $type to a different type when deserialising? [duplicate]

Suppose I have the following class hierarchy:
public abstract class Organization
{
/* properties related to all organizations */
}
public sealed class Company : Organization
{
/* properties related to companies */
}
public sealed class NonProfitOrganization : Organization
{
/* properties related to non profit organizations */
}
Is it possible to have json.net use property (say "type" or "discriminator") to determine which type the object when it deserializes the organization? For example, the following should deserialize an instance of Company.
{
"type": "company"
/* other properties related to companies */
}
And the following should deserialize an instance of NonProfitOrganization.
{
"type": "non-profit"
/* other properties related to non profit */
}
When I call the following:
Organization organization = JsonConvert.DeserializeObject<Organization>(payload);
where payload is the above JSON snippets. I had a look at setting the "TypeNameHandling" on properties or classes but it serializes the whole .NET type, which isn't "portable" between the client and server when the classes are defined in different namespaces and assemblies.
I'd rather define the type is a neutral manner which clients written in any language can use to determine the actual type of the object type being serialized.
In case you are still looking, here is an example: http://james.newtonking.com/archive/2011/11/19/json-net-4-0-release-4-bug-fixes.aspx
This will allow you to create a table based mapping:
public class TypeNameSerializationBinder : SerializationBinder
{
public TypeNameSerializationBinder(Dictionary<Type, string> typeNames = null)
{
if (typeNames != null)
{
foreach (var typeName in typeNames)
{
Map(typeName.Key, typeName.Value);
}
}
}
readonly Dictionary<Type, string> typeToName = new Dictionary<Type, string>();
readonly Dictionary<string, Type> nameToType = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public void Map(Type type, string name)
{
this.typeToName.Add(type, name);
this.nameToType.Add(name, type);
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
var name = typeToName.Get(serializedType);
if (name != null)
{
assemblyName = null;
typeName = name;
}
else
{
assemblyName = serializedType.Assembly.FullName;
typeName = serializedType.FullName;
}
}
public override Type BindToType(string assemblyName, string typeName)
{
if (assemblyName == null)
{
var type = this.nameToType.Get(typeName);
if (type != null)
{
return type;
}
}
return Type.GetType(string.Format("{0}, {1}", typeName, assemblyName), true);
}
}
The code has a slight defect in that if a type name mapping is attempted where the type is unique but the name is already used, the Map method will throw an exception after the type-to-name mapping is already added leaving the table in an inconsistent state.
To take eulerfx's answer further; I wanted to apply DisplayName attribute to a class and have that automatically become the type name used; to that end:
public class DisplayNameSerializationBinder : DefaultSerializationBinder
{
private Dictionary<string, Type> _nameToType;
private Dictionary<Type, string> _typeToName;
public DisplayNameSerializationBinder()
{
var customDisplayNameTypes =
this.GetType()
.Assembly
//concat with references if desired
.GetTypes()
.Where(x => x
.GetCustomAttributes(false)
.Any(y => y is DisplayNameAttribute));
_nameToType = customDisplayNameTypes.ToDictionary(
t => t.GetCustomAttributes(false).OfType<DisplayNameAttribute>().First().DisplayName,
t => t);
_typeToName = _nameToType.ToDictionary(
t => t.Value,
t => t.Key);
}
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
if (false == _typeToName.ContainsKey(serializedType))
{
base.BindToName(serializedType, out assemblyName, out typeName);
return;
}
var name = _typeToName[serializedType];
assemblyName = null;
typeName = name;
}
public override Type BindToType(string assemblyName, string typeName)
{
if (_nameToType.ContainsKey(typeName))
return _nameToType[typeName];
return base.BindToType(assemblyName, typeName);
}
}
and usage example:
public class Parameter
{
public string Name { get; set; }
};
[DisplayName("bool")]
public class BooleanParameter : Parameter
{
}
[DisplayName("string")]
public class StringParameter : Parameter
{
public int MinLength { get; set; }
public int MaxLength { get; set; }
}
[DisplayName("number")]
public class NumberParameter : Parameter
{
public double Min { get; set; }
public double Max { get; set; }
public string Unit { get; set; }
}
[DisplayName("enum")]
public class EnumParameter : Parameter
{
public string[] Values { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var parameters = new Parameter[]
{
new BooleanParameter() {Name = "alive"},
new StringParameter() {Name = "name", MinLength = 0, MaxLength = 10},
new NumberParameter() {Name = "age", Min = 0, Max = 120},
new EnumParameter() {Name = "status", Values = new[] {"Single", "Married"}}
};
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
Binder = new DisplayNameSerializationBinder(),
TypeNameHandling = TypeNameHandling.Auto,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
Formatting = Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var json = JsonConvert.SerializeObject(parameters);
var loadedParams = JsonConvert.DeserializeObject<Parameter[]>(json);
Console.WriteLine(JsonConvert.SerializeObject(loadedParams));
}
}
output:
[
{
"$type": "bool",
"name": "alive"
},
{
"$type": "string",
"maxLength": 10,
"name": "name"
},
{
"$type": "number",
"max": 120.0,
"name": "age"
},
{
"$type": "enum",
"values": [
"Single",
"Married"
],
"name": "status"
}
]
I've written purely declarative solution with ability to specify custom discriminator field, and provide scoped name handling per base class (as opposed to usecure global JsonSerializationSettings, especially on different Web-Api when we do not have ability to specify custom JsonSerializationSettings).
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
// Discriminated Json Converter (JsonSubtypes) implementation for .NET
//
// MIT License
//
// Copyright (c) 2016 Anatoly Ressin
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
////////////////////// USAGE ////////////////////////////////////////////////////////////////////////////////
[JsonConverter(typeof(JsonSubtypes))] // Discriminated base class SHOULD NOT be abstract
public class ShapeBase {
[JsonTag, JsonProperty("#type")] // it SHOULD contain a property marked with [JsonTag]
public string Type {get;set;} // only one [JsonTag] annotation allowed per discriminated class
// it COULD contain other properties, however this is NOT RECOMMENDED
// Rationale: instances of this class will be created at deserialization
// only for tag sniffing, and then thrown away.
}
public abstract class Shape: ShapeBase { // If you want abstract parent - extend the root
public abstract double GetArea(); // with needed abstract stuff, then use this class everywhere (see DEMO below)
}
[JsonSubtype("circle")] // Every final class-case SHOULD be marked with [JsonSubtype(tagValue)]
public class Circle: Shape { // Two disctinct variant classes MUST have distinct tagValues
[JsonProperty("super-radius")] // You CAN use any Json-related annotation as well
public double Radius { get; set; }
public override double GetArea() {
return Radius * Radius * Math.PI;
}
}
[JsonSubtype("rectangle")]
public class Rectangle: Shape {
public double Height { get; set; }
public double Width { get; set; }
public override double GetArea() {
return Width * Height;
}
}
[JsonSubtype("group")]
public class Group: Shape {
[JsonProperty("shapes")]
public List<Shape> Items { get; set; }
public override double GetArea() {
return Items.Select(item => item.GetArea()).Sum();
}
}
// Every final class-case SHOULD be registered with JsonSubtypes.register(typeof(YourConcreteClass))
// either manually or with auto-register capability:
// You can auto-register all classes marked with [JsonSubtype(tag)] in given Assembly
// using JsonSubtypes.autoRegister(yourAssembly)
////////////////// DEMO /////////////////////////////////////////////////////////////////////////////////
public class Program
{
public static void Main()
{
JsonSubtypes.autoRegister(Assembly.GetExecutingAssembly());
Shape original = new Group() {
Items = new List<Shape> {
new Circle() { Radius = 5 },
new Rectangle() { Height = 10, Width = 20 }
}
};
string str = JsonConvert.SerializeObject(original);
Console.WriteLine(str);
var copy = JsonConvert.DeserializeObject(str,typeof(Shape)) as Shape;
// Note: we can deserialize object using any class from the hierarchy.
// Under the hood, anyway, it will be deserialized using the top-most
// base class annotated with [JsonConverter(typeof(JsonSubtypes))].
// Thus, only soft-casts ("as"-style) are safe here.
Console.WriteLine("original.area = {0}, copy.area = {1}", original.GetArea(), copy.GetArea());
}
}
//////////////////////// IMPLEMENTATION //////////////////////////////////////////////////////////////////
public class JsonSubtypeClashException: Exception {
public string TagValue { get; private set;}
public Type RootType { get; private set; }
public Type OldType { get; private set; }
public Type NewType { get; private set; }
public JsonSubtypeClashException(Type rootType, string tagValue, Type oldType, Type newType): base(
String.Format(
"JsonSubtype Clash for {0}[tag={1}]: oldType = {2}, newType = {3}",
rootType.FullName,
tagValue,
oldType.FullName,
newType.FullName
)
) {
TagValue = tagValue;
RootType = rootType;
OldType = oldType;
NewType = newType;
}
}
public class JsonSubtypeNoRootException: Exception {
public Type SubType { get; private set; }
public JsonSubtypeNoRootException(Type subType): base(
String.Format(
"{0} should be inherited from the class with the [JsonConverter(typeof(JsonSubtypes))] attribute",
subType.FullName
)
) {
SubType = subType;
}
}
public class JsonSubtypeNoTagException: Exception {
public Type SubType { get; private set; }
public JsonSubtypeNoTagException(Type subType): base(
String.Format(
#"{0} should have [JsonSubtype(""..."")] attribute",
subType.FullName
)
) {
SubType = subType;
}
}
public class JsonSubtypeNotRegisteredException: Exception {
public Type Root { get; private set; }
public string TagValue { get; private set; }
public JsonSubtypeNotRegisteredException(Type root, string tagValue): base(
String.Format(
#"Unknown tag={1} for class {0}",
root.FullName,
tagValue
)
) {
Root = root;
TagValue = tagValue;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class JsonSubtypeAttribute: Attribute {
private string tagValue;
public JsonSubtypeAttribute(string tagValue) {
this.tagValue = tagValue;
}
public string TagValue {
get {
return tagValue;
}
}
}
public static class JsonSubtypesExtension {
public static bool TryGetAttribute<T>(this Type t, out T attribute) where T: Attribute {
attribute = t.GetCustomAttributes(typeof(T), false).Cast<T>().FirstOrDefault();
return attribute != null;
}
private static Dictionary<Type, PropertyInfo> tagProperties = new Dictionary<Type, PropertyInfo>();
public static bool TryGetTagProperty(this Type t, out PropertyInfo tagProperty) {
if (!tagProperties.TryGetValue(t, out tagProperty)) {
JsonConverterAttribute conv;
if (t.TryGetAttribute(out conv) && conv.ConverterType == typeof(JsonSubtypes)) {
var props = (from prop in t.GetProperties() where prop.GetCustomAttribute(typeof(JsonTagAttribute)) != null select prop).ToArray();
if (props.Length == 0) throw new Exception("No tag");
if (props.Length > 1) throw new Exception("Multiple tags");
tagProperty = props[0];
} else {
tagProperty = null;
}
tagProperties[t] = tagProperty;
}
return tagProperty != null;
}
public static bool TryGetTagValue(this Type t, out string tagValue) {
JsonSubtypeAttribute subtype;
if (t.TryGetAttribute(out subtype)) {
tagValue = subtype.TagValue;
return true;
} else {
tagValue = null;
return false;
}
}
public static bool TryGetJsonRoot(this Type t, out Type root, out PropertyInfo tagProperty) {
root = t;
do {
if (root.TryGetTagProperty(out tagProperty)) {
return true;
}
root = root.BaseType;
} while (t != null);
return false;
}
}
public class JsonTagAttribute: Attribute {
}
public class JsonTagInfo {
public PropertyInfo Property { get; set; }
public string Value { get; set; }
}
public class JsonRootInfo {
public PropertyInfo Property { get; set; }
public Type Root { get; set; }
}
public abstract class DefaultJsonConverter: JsonConverter {
[ThreadStatic]
private static bool silentWrite;
[ThreadStatic]
private static bool silentRead;
public sealed override bool CanWrite {
get {
var canWrite = !silentWrite;
silentWrite = false;
return canWrite;
}
}
public sealed override bool CanRead {
get {
var canRead = !silentRead;
silentRead = false;
return canRead;
}
}
protected void _WriteJson(JsonWriter writer, Object value, JsonSerializer serializer) {
silentWrite = true;
serializer.Serialize(writer, value);
}
protected Object _ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) {
silentRead = true;
return serializer.Deserialize(reader, objectType);
}
}
public class JsonSubtypes: DefaultJsonConverter {
private static Dictionary<Type, Dictionary<string, Type>> implementations = new Dictionary<Type, Dictionary<string, Type>>();
private static Dictionary<Type, JsonTagInfo> tags = new Dictionary<Type, JsonTagInfo>();
private static Dictionary<Type, JsonRootInfo> roots = new Dictionary<Type, JsonRootInfo>();
public static void register(Type newType) {
PropertyInfo tagProperty;
Type root;
if (newType.TryGetJsonRoot(out root, out tagProperty)) {
for(var t = newType; t != root; t = t.BaseType) {
roots[t] = new JsonRootInfo() {
Property = tagProperty,
Root = root
};
}
roots[root] = new JsonRootInfo() {
Property = tagProperty,
Root = root
};
Dictionary<string, Type> implementationMap;
if (!implementations.TryGetValue(root, out implementationMap)) {
implementationMap = new Dictionary<string, Type>();
implementations[root] = implementationMap;
}
JsonSubtypeAttribute attr;
if (!newType.TryGetAttribute(out attr)) {
throw new JsonSubtypeNoTagException(newType);
}
var tagValue = attr.TagValue;
Type oldType;
if (implementationMap.TryGetValue(tagValue, out oldType)) {
throw new JsonSubtypeClashException(root, tagValue, oldType, newType);
}
implementationMap[tagValue] = newType;
tags[newType] = new JsonTagInfo() {
Property = tagProperty,
Value = tagValue
};
} else {
throw new JsonSubtypeNoRootException(newType);
}
}
public static void autoRegister(Assembly assembly) {
foreach(var type in assembly.GetTypes().Where(type => type.GetCustomAttribute<JsonSubtypeAttribute>() != null)) {
register(type);
}
}
public override bool CanConvert(Type t) {
return true;
}
public static T EnsureTag<T>(T value) {
JsonTagInfo tagInfo;
if (tags.TryGetValue(value.GetType(), out tagInfo)) {
tagInfo.Property.SetValue(value, tagInfo.Value);
}
return value;
}
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer) {
_WriteJson(writer, EnsureTag(value), serializer);
}
public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) {
JsonTagInfo tagInfo;
if (tags.TryGetValue(objectType, out tagInfo)) {
return _ReadJson(reader, objectType, existingValue, serializer);
} else {
JsonRootInfo rootInfo;
if (roots.TryGetValue(objectType, out rootInfo)) {
JToken t = JToken.ReadFrom(reader);
var stub = _ReadJson(t.CreateReader(), rootInfo.Root, existingValue, serializer);
var tagValue = rootInfo.Property.GetValue(stub) as string;
var implementationMap = implementations[rootInfo.Root];
Type implementation;
if (implementationMap.TryGetValue(tagValue, out implementation)) {
return ReadJson(t.CreateReader(), implementation, null, serializer);
} else {
throw new JsonSubtypeNotRegisteredException(rootInfo.Root, tagValue);
}
} else {
return _ReadJson(reader, objectType, existingValue, serializer);
}
}
}
public static T Deserialize<T>(string s) where T: class {
return JsonConvert.DeserializeObject(s, typeof(T)) as T;
}
public static string Serialize<T>(T value) where T: class {
return JsonConvert.SerializeObject(value);
}
}
output:
{"shapes":[{"super-radius":5.0,"#type":"circle"},{"Height":10.0,"Width":20.0,"#type":"rectangle"}],"#type":"group"}
original.area = 278.539816339745, copy.area = 278.539816339745
You can grab it here:
https://dotnetfiddle.net/ELcvnk
With another JsonSubtypes converter implementation.
Usage:
[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
public virtual string Sound { get; }
public string Color { get; set; }
}
public class Dog : Animal
{
public override string Sound { get; } = "Bark";
public string Breed { get; set; }
}
public class Cat : Animal
{
public override string Sound { get; } = "Meow";
public bool Declawed { get; set; }
}
[TestMethod]
public void Demo()
{
var input = #"{""Sound"":""Bark"",""Breed"":""Jack Russell Terrier""}"
var animal = JsonConvert.DeserializeObject<Animal>(input);
Assert.AreEqual("Jack Russell Terrier", (animal as Dog)?.Breed);
}
the converter implementation can be directly downloaded from the repository: JsonSubtypes.cs and is also availble as a nuget package
Use this JsonKnownTypes, it's very similar way to use, add couple of attribute:
[JsonConverter(typeof(JsonKnownTypeConverter<Organization>))]
[JsonDiscriminator(Name = "discriminator")]
[JsonKnownType(typeof(Company), "company")]
[JsonKnownType(typeof(NonProfitOrganization), "non-profit")]
public abstract class Organization
{
/* properties related to all organizations */
}
public sealed class Company : Organization
{
/* properties related to companies */
}
public sealed class NonProfitOrganization : Organization
{
/* properties related to non profit organizations */
}
And serialize:
var json = JsonConvert.SerializeObject(youObject)
Output json:
{..., "discriminator":"non-profit"} //if object was NonProfitOrganization
Deserialization:
var organization = JsonConvert.DeserializeObject<Organization>(payload);

Parse string as json array from Postgre

I have a table in PostgreSQL with 2 columns - Id and coord.
Column "coord" - geo coordinates stored as a string in JSON format.
Example:
[{"lat":49.09693425316379,"lng":33.61747393628419},{"lat":49.11835977646441,"lng":33.638456496907},{"lat":49.12103137811804,"lng":33.63866144845382},{"lat":49.09694682809236,"lng":33.61746879914138},{"lat":49.08920750204137,"lng":33.61734796797724},{"lat":49.07643862058337,"lng":33.61246117651179}]
How to send such string as JSON Array of objects(POST request).
Entity without getters and setters
public class Lepcoord implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 30)
#Column(name = "tplnr")
private String tplnr;
#Size(max = 2147483647)
#Column(name = "coord")
private String coord;
Controller
#POST
#RequestMapping(value= "/lep/{voltage}", method = RequestMethod.POST, headers = "Accept=application/json")
#ResponseBody
public ResponseEntity<List<Lepcoord>> lep (#PathVariable String voltage)
{
return new ResponseEntity<>(gisDaoService.lep(voltage), HttpStatus.OK);
}
And service
#Transactional(readOnly = true)
public List <Lepcoord> lep (String voltage) {
Query query = this.em.createQuery(
" From Lepcoord ");
List <Lepcoord> rez = null;
try {
rez = (List<Lepcoord>) query.getResultList();
} catch (PersistenceException r) {
return null;
}
return rez;
}
Hibernate cant handle json type If i storeing coord as json in Postgre. May be someone knows easier way. Not to write own classes to work with Postgres json type
You are using Hibernate so it is good to use a custom UserType which knows how to handle json.
create a hibernate usertype
public class GeoJsonType implements UserType
{
protected static final int[] SQL_TYPES = { java.sql.Types.VARCHAR };
#Override
public int[] sqlTypes()
{
return SQL_TYPES;
}
#Override
public Class returnedClass()
{
return GeoEntity.class;
}
#Override
public boolean equals(Object x, Object y) throws HibernateException
{
if (x == y)
{
return true;
}
else if (x == null || y == null)
{
return false;
}
else
{
return x.equals(y);
}
}
#Override
public int hashCode(Object x) throws HibernateException
{
return x.hashCode();
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException
{
// if (rs.wasNull())
// {
// return null;
// }
//this is your json stored in db
String rsArr = rs.getString(names[0]);
if (rsArr == null)
return null;
GeoEntity detailAttr = JSON.toObject(rsArr, GeoEntity.class, null);
return detailAttr;
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException
{
if (value == null)
{
st.setNull(index, SQL_TYPES[0]);
}
else
{
//when stroing object into db convert it to json
GeoEntity castObject = (GeoEntity) value;
String json = JSON.toJson(castObject);
st.setString(index, json);
}
}
#Override
public Object deepCopy(Object value) throws HibernateException
{
return value;
}
#Override
public boolean isMutable()
{
return true;
}
#Override
public Serializable disassemble(Object value) throws HibernateException
{
return null;
}
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return null;
}
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
}
Your Entity.java
#Type(type = "FQN to your GeoJsonType")
#Column(name = "geo")
public GeoEntity getGeo()
{
return geo;
}
Postgres supports the json_to_array function that should be of help here. Take a look at the documentation here.
Alternatively, there is this answer on SO: How to turn a json array into rows in postgres that could point you in the right direction.

How can I map postgresql json data type using Hibernate?

I am following the example mentioned in the below URL ?
Mapping PostgreSQL JSON column to a Hibernate entity property
But always get the following exception:
Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: 2000
at org.hibernate.dialect.TypeNames.get(TypeNames.java:76)
at org.hibernate.dialect.TypeNames.get(TypeNames.java:99)
at org.hibernate.dialect.Dialect.getTypeName(Dialect.java:310)
at org.hibernate.mapping.Column.getSqlType(Column.java:226)
at org.hibernate.mapping.Table.validateColumns(Table.java:369)
at org.hibernate.cfg.Configuration.validateSchema(Configuration.java:1305)
at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:155)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:512)
I am using TomEE as the Server. and trying to store Json body to a postgresql column. I am trying to map the entity pojos to the postgres datatype structure.
Any idea what could be the issue ? or does any has a better technique to handle such as scenario ? Please point me to that source.
The script used to create the entity table is:
CREATE TABLE historyentity
(
id character varying(255) NOT NULL,
userid character varying(255),
lastchanged timestamp without time zone,
type character varying(255),
history json [],
CONSTRAINT historyentity_pkey PRIMARY KEY (id),
CONSTRAINT historyentity_userid_fkey FOREIGN KEY (userid)
REFERENCES userentity (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE historyentity
OWNER TO postgres;
GRANT ALL ON TABLE historyentity TO postgres;
Entity Pojos look like as follows:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#TypeDefs({ #TypeDef(name = "StringJsonObject", typeClass = StringJsonUserType.class) })
public class HistoryEntity {
#Id
private String id;
private String userid;
private String type;
#Type(type = "StringJsonObject")
private String history;
private Date lastchanged;
}
I am using lombok to define the entity pojos.
Following is the Dialect extended class:
I have tried with both the registered types, Column and Hibenate. But both are not working out.
import org.hibernate.dialect.PostgreSQL82Dialect;
public class JsonPostgreSQLDialect extends PostgreSQL82Dialect
{
#Inject
public JsonPostgreSQLDialect()
{
super();
this.registerColumnType(Types.JAVA_OBJECT, "json");
// this.registerHibernateType(Types.JAVA_OBJECT, "json");
}
}
The following class is being used to define the User Type:
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;
public class StringJsonUserType implements UserType
{
private final int[] sqlTypesSupported = new int[]{ Types.JAVA_OBJECT };
/**
* Return the SQL type codes for the columns mapped by this type. The codes are defined on <tt>java.sql.Types</tt>.
*
* #return int[] the typecodes
* #see java.sql.Types
*/
#Override
public int[] sqlTypes()
{
return sqlTypesSupported;
}
/**
* The class returned by <tt>nullSafeGet()</tt>.
*
* #return Class
*/
#Override
public Class returnedClass()
{
return String.class;
}
/**
* Compare two instances of the class mapped by this type for persistence "equality". Equality of the persistent
* state.
*
* #return boolean
*/
#Override
public boolean equals(Object x, Object y) throws HibernateException
{
if (x == null)
{
return y == null;
}
return x.equals(y);
}
/**
* Get a hashcode for the instance, consistent with persistence "equality"
*/
#Override
public int hashCode(Object x) throws HibernateException
{
return x.hashCode();
}
/**
* Retrieve an instance of the mapped class from a JDBC resultset. Implementors should handle possibility of null
* values.
*
* #param rs a JDBC result set
* #param names the column names
* #param owner the containing entity #return Object
*/
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
throws HibernateException, SQLException
{
if (rs.getString(names[0]) == null)
{
return null;
}
return rs.getString(names[0]);
}
/**
* Write an instance of the mapped class to a prepared statement. Implementors should handle possibility of null
* values. A multi-column type should be written to parameters starting from <tt>index</tt>.
*
* #param st a JDBC prepared statement
* #param value the object to write
* #param index statement parameter index
*/
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
throws HibernateException, SQLException
{
if (value == null)
{
st.setNull(index, Types.OTHER);
return;
}
st.setObject(index, value, Types.OTHER);
}
/**
* Return a deep copy of the persistent state, stopping at entities and at collections. It is not necessary to copy
* immutable objects, or null values, in which case it is safe to simply return the argument.
*
* #param value the object to be cloned, which may be null
* #return Object a copy
*/
#Override
public Object deepCopy(Object value) throws HibernateException
{
return value;
}
/**
* Are objects of this type mutable?
*
* #return boolean
*/
#Override
public boolean isMutable()
{
return true;
}
/**
* Transform the object into its cacheable representation. At the very least this method should perform a deep copy
* if the type is mutable. That may not be enough for some implementations, however; for example, associations must
* be cached as identifier values. (optional operation)
*
* #param value the object to be cached
* #return a cachable representation of the object
*/
#Override
public Serializable disassemble(Object value) throws HibernateException
{
return (String) this.deepCopy(value);
}
/**
* Reconstruct an object from the cacheable representation. At the very least this method should perform a deep copy
* if the type is mutable. (optional operation)
*
* #param cached the object to be cached
* #param owner the owner of the cached object
* #return a reconstructed object from the cachable representation
*/
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return this.deepCopy(cached);
}
/**
* During merge, replace the existing (target) value in the entity we are merging to with a new (original) value
* from the detached entity we are merging. For immutable objects, or null values, it is safe to simply return the
* first parameter. For mutable objects, it is safe to return a copy of the first parameter. For objects with
* component values, it might make sense to recursively replace component values.
*
* #param original the value from the detached entity being merged
* #param target the value in the managed entity
* #return the value to be merged
*/
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
}
This work for me :
Your Entity :
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#TypeDef(name = "json", typeClass = JSONUserType.class, parameters = {
#Parameter(name = JSONUserType.CLASS, value = "java.lang.String")})
public class HistoryEntity {
#Id
private String id;
private String userid;
private String type;
#Type(type = "json")
private String history;
private Date lastchanged;
}
Implement Hibernate ParameterizedType and UserType to ensure the conversion between the 2 types (json <->string)
public class JSONUserType implements ParameterizedType, UserType {
private static final ObjectMapper objectMapper = new ObjectMapper();
private static final ClassLoaderService classLoaderService = new ClassLoaderServiceImpl();
public static final String JSON_TYPE = "json";
public static final String CLASS = "CLASS";
private Class jsonClassType;
#Override
public Class<Object> returnedClass() {
return Object.class;
}
#Override
public int[] sqlTypes() {
return new int[]{Types.JAVA_OBJECT};
}
#Override
public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
try {
final String json = resultSet.getString(names[0]);
return json == null ? null : objectMapper.readValue(json, jsonClassType);
} catch (IOException e) {
throw new HibernateException(e);
}
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
try {
final String json = value == null ? null : objectMapper.writeValueAsString(value);
PGobject pgo = new PGobject();
pgo.setType(JSON_TYPE);
pgo.setValue(json);
st.setObject(index, pgo);
} catch (JsonProcessingException e) {
throw new HibernateException(e);
}
}
#Override
public void setParameterValues(Properties parameters) {
final String clazz = (String) parameters.get(CLASS);
jsonClassType = classLoaderService.classForName(clazz);
}
#SuppressWarnings("unchecked")
#Override
public Object deepCopy(Object value) throws HibernateException {
if (!(value instanceof Collection)) {
return value;
}
Collection<?> collection = (Collection) value;
Collection collectionClone = CollectionFactory.newInstance(collection.getClass());
collectionClone.addAll(collection.stream().map(this::deepCopy).collect(Collectors.toList()));
return collectionClone;
}
static final class CollectionFactory {
#SuppressWarnings("unchecked")
static <E, T extends Collection<E>> T newInstance(Class<T> collectionClass) {
if (List.class.isAssignableFrom(collectionClass)) {
return (T) new ArrayList<E>();
} else if (Set.class.isAssignableFrom(collectionClass)) {
return (T) new HashSet<E>();
} else {
throw new IllegalArgumentException("Unsupported collection type : " + collectionClass);
}
}
}
#Override
public boolean isMutable() {
return true;
}
#Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
}
if ((x == null) || (y == null)) {
return false;
}
return x.equals(y);
}
#Override
public int hashCode(Object x) throws HibernateException {
assert (x != null);
return x.hashCode();
}
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return deepCopy(cached);
}
#Override
public Serializable disassemble(Object value) throws HibernateException {
Object deepCopy = deepCopy(value);
if (!(deepCopy instanceof Serializable)) {
throw new SerializationException(String.format("%s is not serializable class", value), null);
}
return (Serializable) deepCopy;
}
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return deepCopy(original);
}
}
And extends PostgreSQL94Dialect class to tell the serializer the matching type:
public class JSONPostgreSQLDialect extends PostgreSQL94Dialect {
public JSONPostgreSQLDialect() {
super();
registerColumnType(Types.JAVA_OBJECT, JSONUserType.JSON_TYPE);
}
}
If you use Spring you must declare this last class in application.properties like this :
spring.jpa.database-platform=com.yourpackage.JSONPostgreSQLDialect
Postgres JSON type has been added to Hibernate in the PostgreSQL92Dialect. So you should either use that dialect or one of its subclasses, or make a custom dialect that adds the following type definition:
this.registerColumnType(2000, "json");
The type itself can be defined as follows (example for Hibernate 5.x):
public class JsonType implements UserType {
public static final ObjectMapper MAPPER = new ObjectMapper();
private int[] sqlTypes;
private com.fasterxml.jackson.databind.ObjectWriter writer;
private JavaType type;
private boolean isBinary;
private ObjectReader reader;
public JsonType() {
init(SimpleType.constructUnsafe(Object.class), false);
}
public JsonType(Class clazz, boolean isBinary) {
this(SimpleType.construct(clazz), isBinary);
}
public JsonType(JavaType type, boolean isBinary) {
init(type, isBinary);
}
protected void init(JavaType type, boolean isBinary) {
this.type = type;
this.isBinary = isBinary;
this.reader = MAPPER.readerFor(type);
this.writer = MAPPER.writerFor(type);
this.sqlTypes = new int[]{Types.JAVA_OBJECT};
}
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
} else if (x == null || y == null) {
return false;
} else {
return x.equals(y);
}
}
public int hashCode(Object x) throws HibernateException {
return null == x ? 0 : x.hashCode();
}
public boolean isMutable() {
return true;
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
final Object result = rs.getObject(names[0]);
if (!rs.wasNull()) {
String content;
if (result instanceof String) {
content = (String) result;
} else if (result instanceof PGobject) {
// If we get directly the PGobject for some reason (more exactly, if a DB like H2 does the serialization directly)
content = ((PGobject) result).getValue();
} else {
throw new IllegalArgumentException("Unknown object type (excepted pgobject or json string)");
}
if (content != null) {
return convertJsonToObject(content);
}
}
return null;
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
if (value == null) {
st.setObject(index, null);
return;
}
PGobject pg = new PGobject();
pg.setType(isBinary ? "jsonb" : "json");
pg.setValue(convertObjectToJson(value));
st.setObject(index, pg);
}
Object convertJsonToObject(String content) {
try {
return reader.readValue(content);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
String convertObjectToJson(Object object) {
try {
return writer.writeValueAsString(object);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public Object deepCopy(Object value) throws HibernateException {
String json = convertObjectToJson(value);
return convertJsonToObject(json);
}
public Object replace(Object original, Object target, Object owner)
throws HibernateException {
return deepCopy(original);
}
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) deepCopy(value);
}
public Object assemble(Serializable cached, Object owner)
throws HibernateException {
return deepCopy(cached);
}
public int[] sqlTypes() {
return sqlTypes;
}
public Class returnedClass() {
return type.getRawClass();
}
}
This example uses Jackson as a framework for JSON (de)serialization.
You can then use your type as follows:
#Entity
#TypeDefs({#TypeDef( name= "StringJsonObject", typeClass = JsonType.class)})
public class MyEntity {
#Type(type = "StringJsonObject")
#Column(name="visuals", columnDefinition = "json")
private Map<String, String> visuals;
}
But this is all very similar to the type that you implemented (presumably for Hibernate 4.x). So why wasn't your implementation working? This is because your field is actually of type json[] (a Postgres array of JSON objects). This mapper only works with JSON objects (type json). This JSON object can very well be a JSON array of JSON objects, but it has to be of type json. So you should change the type in your database schema, or implement a UserType that can work with arrays, but the first option is most likely.

TableModelListener of JTable doesn't fire an event while editing a cell

I have added the following JTable.
public final class EmployeeApp extends JPanel implements ActionListener, TableModelListener
{
private static JTable myTable;
private static JButton btnDelete;
public EmployeeApp()
{
CountryDAO countryDAO=new CountryDAO();
myTable = new JTable(new CountryAbstractTableModel(countryDAO.getList()));
JScrollPane myPane = new JScrollPane(myTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
add(myPane);
myTable.setPreferredScrollableViewportSize(new Dimension(1000, 200));
((CountryAbstractTableModel)myTable.getModel()).addTableModelListener(this);//<---
//Added TableModelListener.
}
#Override
public void tableChanged(TableModelEvent e)
{
int row = e.getFirstRow();
int column = e.getColumn();
CountryAbstractTableModel model = (CountryAbstractTableModel)e.getSource();
Object data = model.getValueAt(row, column);
System.out.println("The tableChanged() method called."); // This is never be seen on the console.
}
}
When a cell is edited, the tableChanged() method should be invoked but it never gets called.
I have extended AbstractTableModel as follows.
package admin.model;
import entity.Country;
import java.util.Iterator;
import java.util.List;
import javax.swing.table.AbstractTableModel;
public final class CountryAbstractTableModel extends AbstractTableModel
{
private List<Country> countries;
public CountryAbstractTableModel(List<Country> countries)
{
this.countries = countries;
}
#Override
public void setValueAt(Object value, int rowIndex, int columnIndex)
{
if(value instanceof Country)
{
Country newCountry=(Country) value;
Country oldCountry = countries.get(rowIndex);
switch (columnIndex)
{
case 2:
oldCountry.setCountryName(newCountry.getCountryName());
break;
case 3:
oldCountry.setCountryCode(newCountry.getCountryCode());
//break;
}
fireTableCellUpdated(rowIndex, columnIndex);
}
}
#Override
public Object getValueAt(int rowIndex, int columnIndex)
{
Country country = countries.get(rowIndex);
switch (columnIndex)
{
case 0:
return rowIndex+1;
case 1:
return country.getCountryId();
case 2:
return country.getCountryName();
case 3:
return country.getCountryCode();
}
return "";
}
#Override
public int getRowCount()
{
return countries.size();
}
#Override
public int getColumnCount()
{
return 4;
}
public void add(Country country)
{
int size = countries.size();
countries.add(country);
fireTableRowsInserted(size, size);
}
public void remove(List<Long>list)
{
Iterator<Country> iterator = countries.iterator();
while(iterator.hasNext())
{
Country country = iterator.next();
Iterator<Long> it = list.iterator();
while(it.hasNext())
{
if(country.getCountryId().equals(it.next()))
{
iterator.remove();
int index = countries.indexOf(country);
fireTableRowsDeleted(index, index);
break;
}
}
}
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return columnIndex>1?true:false;
}
}
In which the setValueAt() method is called when the return key is pressed after editing a cell. Therefore, the tableChanged() method should be called after fireTableCellUpdated() gets called.
Since the first two columns are not editable, there is no need to set the values for them.
Why isn't the tableChanged() method invoked?
Unless you have specifically set a dedicated Country editor for that column, your test value instanceof Country is likely to be false. Most likely value is actually a String. Your setValueAt method should rather look like this:
#Override
public void setValueAt(Object value, int rowIndex, int columnIndex)
{
if(value instanceof String)
{
Country country = countries.get(rowIndex);
String newValue = (String) value;
switch (columnIndex)
{
case 2:
country.setCountryName(newValue); break;
case 3:
country.setCountryCode(newValue); break;
}
fireTableCellUpdated(rowIndex, columnIndex);
}
}
Of course, if the type of countryName and countryCode is not String, you should return appropriate values for the method TableModel.getColumnClass and test for appropriate types in setValueAt

Entity Framework type case generic in predicate

I am working on updating to a more manageable repository pattern in my MVC 4 project that uses Entity Framework code first. I've integrated a generic base repository class that will do basic CRUD operations so I don't have to implement these in each repository I create. I have ran into an issue where my All method needs to filter there query by a deleted flag if the entity is a type of TrackableEntity. Since the Entity is generic in the base repository I am attempting to cast is to a type of TrackableEntity in the where which just results in the following error message.
The 'TypeAs' expression with an input of type 'NameSpace.Models.ClientFormField' and a check of type 'NameSpace.Models.TrackableEntity' is not supported. Only entity types and complex types are supported in LINQ to Entities queries.
This error makes complete since and I understand why the code I have is not working but I am trying to find a way to filter out deleted items without having to override this method in all of my repositories. The code I have for my All method is below.
public virtual IEnumerable<T> All()
{
if (typeof(T).IsSubclassOf(typeof(TrackableEntity)))
return dbSet.Where(e => !(e as TrackableEntity).IsDeleted).ToList();
return dbSet.ToList();
}
I know that I can do the following
public virtual IEnumerable<T> All(Expression<Func<T, bool>> predicate = null)
{
if (predicate != null)
return dbSet.Where(predicate).IsDeleted).ToList();
return dbSet.ToList();
}
And then add this to all of my repositories
public override IEnumerable<CaseType> All(Expression<Func<CaseType,bool>> predicate = null)
{
if (predicate == null)
predicate = e => !e.IsDeleted;
return base.All(predicate);
}
The problem I have with this is that I am duplicating code, this is basically a copy and paste into all of my repositories which defeats the purpose of changing to this new repository pattern. I made the switch to end duplicated code in my repositories.
Here is an example of one of my entities.
public class CaseType : TrackableEntity, IValidatableObject
{
public int Id { get; set; }
public string Name { get; set; }
public bool InUse { get; set; }
public bool IsValid { get { return !this.Validate(null).Any(); } }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (String.IsNullOrEmpty(Name))
yield return new ValidationResult("Case Type name cannot be blank", new[] { "Name" });
//Finish Validation Rules
}
}
And the TrackableEntity
public abstract class TrackableEntity
{
public bool Active { get; set; }
public bool IsDeleted { get; set; }
public virtual User CreatedBy { get; set; }
public virtual User ModifiedBy { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
}
Any help on this would be much appreciated.
I finally got a solution working that I am happy with. I ended up making 2 generic repositories. One that is the base repository which deals with all of the calls to the database for my BaseEntity which all entities inherit from. Then I made my 2nd generic repo which is inherits BaesEntity and overrides a few methods to handle the needs of my TrackableEntities. In the end this does what I want by handling the filtering of soft deleted items from within the repo and also gives me more flexibility with the TrackableEntity.
BaseRepository -
public class BaseRepository<T> : IBaseRepository<T> where T : BaseEntity
{
private readonly IAppDb _db;
private readonly IDbSet<T> _dbSet;
public BaseRepository(IAppDb db)
{
_db = db;
_dbSet = Lwdb.Set<T>();
}
protected IAppDb Lwdb
{
get { return _db; }
}
#region IBaseRepository<T> Members
public virtual T GetById(int id)
{
return _dbSet.Find(id);
}
public virtual T Add(T entity)
{
_dbSet.Add(entity);
_db.Commit();
return entity;
}
public virtual bool Any(Expression<Func<T, bool>> expression)
{
return _dbSet.Any(expression);
}
public virtual void Delete(T entity)
{
_dbSet.Remove(entity);
_db.Commit();
}
public virtual IEnumerable<T> All()
{
return _dbSet.ToList();
}
public virtual T Update(T entity, bool attachOnly = false)
{
_dbSet.Attach(entity);
_db.SetModified(entity);
if (!attachOnly) _db.Commit();
return entity;
}
#endregion
protected User GetCurrentUser()
{
return
_db.Set<User>().Find(HttpContext.Current != null ? ((User) HttpContext.Current.Session["User"]).Id : 1);
}
BaseTrackableEntityRepository -
public class BaseTrackableEntityRepository<T> : BaseRepository<T>, IBaseTrackableEntityRepository<T>
where T : TrackableEntity
{
private readonly IAppDb _db;
private readonly IDbSet<T> _teDB;
public BaseTrackableEntityRepository(IAppDb db)
: base(db)
{
_db = db;
_teDB = _db.Set<T>();
}
#region IBaseTrackableEntityRepository<T> Members
public virtual T SetDeleteFlag(int id)
{
var entity = _teDB.Find(id);
if (entity == null) return null; //throw exception
entity.IsDeleted = true;
entity.DateModified = DateTime.Now;
entity.ModifiedBy = GetCurrentUser();
return Update(entity);
}
public override IEnumerable<T> All()
{
return _teDB.Where(e => !e.IsDeleted).ToList();
}
public override T Add(T entity)
{
var curUser = GetCurrentUser();
entity.CreatedBy = curUser;
entity.ModifiedBy = curUser;
entity.DateCreated = DateTime.Now;
entity.DateModified = DateTime.Now;
entity.Active = true;
entity.IsDeleted = false;
_teDB.Add(entity);
_db.Commit();
return entity;
}
public override T Update(T entity, bool attachOnly = false)
{
InsertTeData(ref entity);
entity.ModifiedBy = GetCurrentUser();
entity.DateModified = DateTime.Now;
_teDB.Attach(entity);
_db.SetModified(entity);
if (!attachOnly) _db.Commit();
return entity;
}
public virtual T SetStatus(int id, bool status)
{
var entity = _teDB.Find(id);
if (entity == null) return null;
entity.Active = status;
return Update(entity);
}
#endregion
private void InsertTeData(ref T entity)
{
if (entity == null || entity == null) return;
var dbEntity = GetById(entity.Id);
if (dbEntity == null) return;
_db.Detach(dbEntity);
entity.CreatedBy = dbEntity.CreatedBy;
entity.DateCreated = dbEntity.DateCreated;
entity.ModifiedBy = dbEntity.ModifiedBy;
entity.DateModified = dbEntity.DateModified;
}