Extending Entity Framework 6 - adding custom properties to entities in designer - edmx

The problem:
I want to be able to extend EF6 in a way that enables me to define Caching policy, Transactional policy, Multi-tenancy , etc on a single entity in an EF edmx file. I am willing doing this with designer.
I've managed to make an extension like suggested in http://www.databinding.net/blog/post/2010/12/02/entity-framework-4-benutzerdefinierte-eigenschaften-mit-dem-adonet-entity-data-model-designer-ext.html
but after installing the vsix the properties are not there. Restarted VS etc but not working. Is something I have to do more in VS 2013?
Has somebody done something similar?

It turns out that EF6 extensions are quiet simple to implement. First you have to follow the MSDN docs for your EF version. Then you have to Make 2 project one for the EF extension and the second is the VS extension project (VSIX).
In the EF extension project add two classes:
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Data.Entity.Design.Extensibility;
namespace SaopEdmxExtenstions
{
[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(IEntityDesignerExtendedProperty))]
[EntityDesignerExtendedProperty(EntityDesignerSelection.ConceptualModelEntityType)]
public class EfDesignerCustomPropertiesFactory : IEntityDesignerExtendedProperty
{
public object CreateProperty(XElement element, PropertyExtensionContext context)
{
return new EfDesignerCustomProperties(element, context);
}
}
public class EfDesignerCustomProperties
{
internal static readonly string Namespace = "http://saop.si";
internal static XName NsBaseDiagram = XName.Get("CachingType", Namespace);
internal const string Category = "Saop Edmx Extensions";
private readonly XElement _parent;
private readonly PropertyExtensionContext _context;
public EfDesignerCustomProperties(XElement parent, PropertyExtensionContext context)
{
_context = context;
_parent = parent;
}
[DisplayName("Caching Type")]
[Description("Specifies how to handle entity Caching")]
[Category(EfDesignerCustomProperties.Category)]
[DefaultValue(CachingType.None)]
public CachingType CustomCachingType
{
get
{
var propertyValue = CachingType.None;
if (!_parent.HasElements) return propertyValue;
foreach (XElement element in _parent.Elements())
{
if (element.Name != EfDesignerCustomProperties.NsBaseDiagram) continue;
var bv = element.Value.Trim();
switch (bv)
{
case "None": propertyValue = CachingType.None; break;
case "Application": propertyValue = CachingType.Application; break;
case "Request": propertyValue = CachingType.Request; break;
case "Session": propertyValue = CachingType.Session; break;
}
}
return propertyValue;
}
set
{
CachingType propertyValue = value;
using (EntityDesignerChangeScope scope = _context.CreateChangeScope("Set Entity Caching"))
{
if (_parent.HasElements)
{
var updated = false;
foreach (XElement element in _parent.Elements())
{
if (element.Name != EfDesignerCustomProperties.NsBaseDiagram) continue;
element.SetValue(propertyValue.ToString().Trim());
updated = true;
break;
}
if (!updated)
{
var lastChild = _parent.Elements().Last();
lastChild.AddAfterSelf(new XElement(NsBaseDiagram, propertyValue));
}
}
else
{
_parent.Add(new XElement(NsBaseDiagram, propertyValue.ToString().Trim()));
}
scope.Complete();
}
}
}
[DisplayName("Is Multi-Tenant")]
[Description("Specifies if the entity is multi-tenant")]
[Category(EfDesignerCustomProperties.Category)]
[DefaultValue(false)]
public bool IsMultitTenant
{
get
{
var propertyValue = false;
if (!_parent.HasElements) return propertyValue;
foreach (XElement element in _parent.Elements())
{
if (element.Name != EfDesignerCustomProperties.NsBaseDiagram) continue;
var bv = element.Value.Trim();
switch (bv.ToLower())
{
case "false": propertyValue = false; break;
case "true": propertyValue = true; break;
}
}
return propertyValue;
}
set
{
var propertyValue = value;
using (EntityDesignerChangeScope scope = _context.CreateChangeScope("Set MultiTenancy"))
{
if (_parent.HasElements)
{
var updated = false;
foreach (XElement element in _parent.Elements())
{
if (element.Name != EfDesignerCustomProperties.NsBaseDiagram) continue;
element.SetValue(propertyValue.ToString().Trim());
updated = true;
break;
}
if (!updated)
{
var lastChild = _parent.Elements().Last();
lastChild.AddAfterSelf(new XElement(NsBaseDiagram, propertyValue));
}
}
else
{
_parent.Add(new XElement(NsBaseDiagram, propertyValue.ToString().Trim()));
}
scope.Complete();
}
}
}
public enum CachingType
{
None,
Request,
Session,
Application
}
}
}
see MSDN why is that way. You can define where to hook by setting different value for [EntityDesignerExtendedProperty(EntityDesignerSelection.ConceptualModelEntityType)] , again see MSDN.
I've decided to hook on Entity diagram select.
Then In the VSIX project add an Asset of type Microsoft.VisualStudio.MefComponent and select the EF extension project. Build the solution. Go to bin's (debug or release) and copy all files to a new folder. Add a new xml file named [Content_Types].xml with the sample content of:
<?xml version="1.0" encoding="utf-8" ?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="vsixmanifest" ContentType="text/xml" />
<Default Extension="dll" ContentType="application/octet-stream" />
<Default Extension="png" ContentType="application/octet-stream" />
<Default Extension="txt" ContentType="text/plain" />
<Default Extension="pkgdef" ContentType="text/plain" />
</Types>
Then compress all as a zip and rename it so xxxx.vsix. Install it to VS (defined as Install Target in the VSIX project).
Restart VS and go to an edmx of your choice. You will se the results like this

Related

CustomResourceHandler -> Version number is added to resources twice

I've written an CustomResourceHandler to add a version number to my js and css files, which works fine, but all primefaces resources get a double version number now.
Without CustomResourceHandler:
javax.faces.resource/jquery/jquery-plugins.js.xhtml?ln=primefaces&v=6.0.17
With CustomResourceHandler:
javax.faces.resource/jquery/jquery-plugins.js.xhtml?ln=primefaces&v=6.0.17&v=6.0.17
My CustomResourceHandler:
public class ExtendedResourceHandler extends PrimeResourceHandler {
public ExtendedResourceHandler(ResourceHandler wrapped) {
super(wrapped);
}
#Override
public Resource createResource(String resourceName, String libraryName) {
if (!org.primefaces.util.Constants.LIBRARY.equalsIgnoreCase(libraryName)
&& !org.primefaces.extensions.util.Constants.LIBRARY.equalsIgnoreCase(libraryName) && !"javax.faces".equalsIgnoreCase(libraryName) && resourceName != null
&& (resourceName.endsWith(".css") || resourceName.endsWith(".js"))) {
final Resource resource = super.createResource(resourceName, libraryName);
if (resource == null) {
return null;
}
return new ResourceWrapper() {
#Override
public String getRequestPath() {
String resultPath = super.getRequestPath();
resultPath += (resultPath.contains("?") ? "&" : "?") + "version=V6326";
return resultPath;
}
#Override
public Resource getWrapped() {
return resource;
}
};
} else {
return super.createResource(resourceName, libraryName);
}
}
}
faces-config.xml:
<resource-handler>de.sync4.cockpit.web.beans.resourcehandler.ExtendedResourceHandler</resource-handler>
Because of that problem I can't get the pe:ckEditor to work, because it can't find necessary resources. Any idea what's wrong with my Handler?
Found the Problem. I needed to extend the ResourceHandlerWrapper, not the PrimeResourceHandler.
I also have an ExtendedBeanELResolver. I had to return null there if the base object is an instance of my custom handler.

How to perform partial object serialization providing "paths" using Newtonsoft JSON.NET

I have a situation in which I have a very large C# object, however, I only need to return a handful of properties (which can be on nested objects), allow for client-side JavaScript to modify those properties and then send the resulting object back to the server in order to perform in-place partial de-serialization.
The idea is to re-use some very large existing business objects, but be intelligent about only serializing and sending only those properties back to the client application for modification (to keep the amount of data transferred at a minimum).
I basically have an XML file where I pre-define all of the bindings using a "path syntax" which would indicate only those properties I need to serialize. So, I could use something like "WorkOrder.UserField1" or "WorkOrder.Client.Name".
I have tried using a custom contract resolver to determine whether or not a property should be serialized; however, it doesn't seem that I have information as to the "path" (in other words, other properties in the object model up the chain) in order to determine if the property should or should not be serialized.
I have also tried using a custom JsonTextWriter, but it doesn't seem that I can override the methods necessary to keep track of the path, even though there is a Path property available. Is there something perhaps simple that I am overlooking in order to be able to view the path hierarchy of a property being serialized and determine if it should be serialized by looking up the path in a table and making the decision?
The basic difficulty here is that Json.NET is a contract-based serializer which creates a contract for each type to be serialized, then (de)serializes according to the contract. If a type appears in multiple locations in the object hierarchy, the same contract applies. But you want to selectively include properties for a given type depending on its location in the hierarchy, which conflicts with the basic "one type one contract" design.
One quick way to work around this is to serialize to a JObject, then use JToken.SelectTokens() to select only the JSON data you want to return, removing everything else. Since SelectTokens has full support for JSONPath query syntax, you can selectively include using array and property wildcards or other filters, for instance:
"$.FirstLevel[*].Bar"
includes all properties named "Bar" in all array members of a property named "FirstLevel" of the root object.
This should reduce your network usage as desired, but won't save any processing time on the server.
Removal can be accomplished with the following extension methods:
public static partial class JsonExtensions
{
public static TJToken RemoveAllExcept<TJToken>(this TJToken obj, IEnumerable<string> paths) where TJToken : JToken
{
if (obj == null || paths == null)
throw new NullReferenceException();
var keepers = new HashSet<JToken>(paths.SelectMany(path => obj.SelectTokens(path)), ObjectReferenceEqualityComparer<JToken>.Default);
var keepersAndParents = new HashSet<JToken>(keepers.SelectMany(t => t.AncestorsAndSelf()), ObjectReferenceEqualityComparer<JToken>.Default);
// Keep any token that is a keeper, or a child of a keeper, or a parent of a keeper
// I.e. if you have a path ""$.A.B" and it turns out that B is an object, then everything
// under B should be kept.
foreach (var token in obj.DescendantsAndSelfReversed().Where(t => !keepersAndParents.Contains(t) && !t.AncestorsAndSelf().Any(p => keepers.Contains(p))))
token.RemoveFromLowestPossibleParent();
// Return the object itself for fluent style programming.
return obj;
}
public static string SerializeAndSelectTokens<T>(T root, string[] paths, Formatting formatting = Formatting.None, JsonSerializerSettings settings = null)
{
var obj = JObject.FromObject(root, JsonSerializer.CreateDefault(settings));
obj.RemoveAllExcept(paths);
var json = obj.ToString(formatting);
return json;
}
public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
JToken toRemove;
var property = node.Parent as JProperty;
if (property != null)
{
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
toRemove = property;
property.Value = null;
}
else
{
toRemove = node;
}
if (toRemove.Parent != null)
toRemove.Remove();
return node;
}
public static IEnumerable<JToken> DescendantsAndSelfReversed(this JToken node)
{
if (node == null)
throw new ArgumentNullException();
return RecursiveEnumerableExtensions.Traverse(node, t => ListReversed(t as JContainer));
}
// Iterate backwards through a list without throwing an exception if the list is modified.
static IEnumerable<T> ListReversed<T>(this IList<T> list)
{
if (list == null)
yield break;
for (int i = list.Count - 1; i >= 0; i--)
yield return list[i];
}
}
public static partial class RecursiveEnumerableExtensions
{
// Rewritten from the answer by Eric Lippert https://stackoverflow.com/users/88656/eric-lippert
// to "Efficient graph traversal with LINQ - eliminating recursion" http://stackoverflow.com/questions/10253161/efficient-graph-traversal-with-linq-eliminating-recursion
// to ensure items are returned in the order they are encountered.
public static IEnumerable<T> Traverse<T>(
T root,
Func<T, IEnumerable<T>> children)
{
yield return root;
var stack = new Stack<IEnumerator<T>>();
try
{
stack.Push((children(root) ?? Enumerable.Empty<T>()).GetEnumerator());
while (stack.Count != 0)
{
var enumerator = stack.Peek();
if (!enumerator.MoveNext())
{
stack.Pop();
enumerator.Dispose();
}
else
{
yield return enumerator.Current;
stack.Push((children(enumerator.Current) ?? Enumerable.Empty<T>()).GetEnumerator());
}
}
}
finally
{
foreach (var enumerator in stack)
enumerator.Dispose();
}
}
}
/// <summary>
/// A generic object comparerer that would only use object's reference,
/// ignoring any <see cref="IEquatable{T}"/> or <see cref="object.Equals(object)"/> overrides.
/// </summary>
public class ObjectReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class
{
// Adapted from this answer https://stackoverflow.com/a/1890230
// to https://stackoverflow.com/questions/1890058/iequalitycomparert-that-uses-referenceequals
// By https://stackoverflow.com/users/177275/yurik
private static readonly IEqualityComparer<T> _defaultComparer;
static ObjectReferenceEqualityComparer() { _defaultComparer = new ObjectReferenceEqualityComparer<T>(); }
public static IEqualityComparer<T> Default { get { return _defaultComparer; } }
#region IEqualityComparer<T> Members
public bool Equals(T x, T y)
{
return ReferenceEquals(x, y);
}
public int GetHashCode(T obj)
{
return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
}
#endregion
}
And then use them like:
public class TestClass
{
public static void Test()
{
var root = new RootObject
{
FirstLevel1 = new FirstLevel
{
SecondLevel1 = new List<SecondLevel> { new SecondLevel { A = "a11", B = "b11", Third1 = new ThirdLevel { Foo = "Foos11", Bar = "Bars11" }, Third2 = new List<ThirdLevel> { new ThirdLevel { Foo = "FooList11", Bar = "BarList11" } } } },
SecondLevel2 = new List<SecondLevel> { new SecondLevel { A = "a12", B = "b12", Third1 = new ThirdLevel { Foo = "Foos12", Bar = "Bars12" }, Third2 = new List<ThirdLevel> { new ThirdLevel { Foo = "FooList12", Bar = "BarList12" } } } },
},
FirstLevel2 = new FirstLevel
{
SecondLevel1 = new List<SecondLevel> { new SecondLevel { A = "a21", B = "b21", Third1 = new ThirdLevel { Foo = "Foos21", Bar = "Bars21" }, Third2 = new List<ThirdLevel> { new ThirdLevel { Foo = "FooList21", Bar = "BarList21" } } } },
SecondLevel2 = new List<SecondLevel> { new SecondLevel { A = "a22", B = "b22", Third1 = new ThirdLevel { Foo = "Foos22", Bar = "Bars22" }, Third2 = new List<ThirdLevel> { new ThirdLevel { Foo = "FooList22", Bar = "BarList22" } } } },
}
};
Assert.IsTrue(JObject.FromObject(root).DescendantsAndSelf().OfType<JValue>().Count() == 24); // No assert
var paths1 = new string[]
{
"$.FirstLevel2.SecondLevel1[*].A",
"$.FirstLevel1.SecondLevel2[*].Third2[*].Bar",
};
Test(root, paths1, 2);
var paths3 = new string[]
{
"$.FirstLevel1.SecondLevel2[*].Third2[*].Bar",
};
Test(root, paths3, 1);
var paths4 = new string[]
{
"$.*.SecondLevel2[*].Third2[*].Bar",
};
Test(root, paths4, 2);
}
static void Test<T>(T root, string [] paths, int expectedCount)
{
var json = JsonExtensions.SerializeAndSelectTokens(root, paths, Formatting.Indented);
Console.WriteLine("Result using paths: {0}", JsonConvert.SerializeObject(paths));
Console.WriteLine(json);
Assert.IsTrue(JObject.Parse(json).DescendantsAndSelf().OfType<JValue>().Count() == expectedCount); // No assert
}
}
public class ThirdLevel
{
public string Foo { get; set; }
public string Bar { get; set; }
}
public class SecondLevel
{
public ThirdLevel Third1 { get; set; }
public List<ThirdLevel> Third2 { get; set; }
public string A { get; set; }
public string B { get; set; }
}
public class FirstLevel
{
public List<SecondLevel> SecondLevel1 { get; set; }
public List<SecondLevel> SecondLevel2 { get; set; }
}
public class RootObject
{
public FirstLevel FirstLevel1 { get; set; }
public FirstLevel FirstLevel2 { get; set; }
}
Note that there is an enhancement request Feature request: ADD JsonProperty.ShouldSerialize(object target, string path) #1857 that would enable this sort of functionality more easily.
Demo fiddles here and here.
The much easier implementation (comparing to the accepted answer) is presented here:
public static class JsonExtensions
{
public static TJToken RemoveAllExcept<TJToken>(this TJToken token, IEnumerable<string> paths) where TJToken : JContainer
{
HashSet<JToken> nodesToRemove = new(ReferenceEqualityComparer.Instance);
HashSet<JToken> nodesToKeep = new(ReferenceEqualityComparer.Instance);
foreach (var whitelistedToken in paths.SelectMany(token.SelectTokens))
TraverseTokenPath(whitelistedToken, nodesToRemove, nodesToKeep);
//In that case neither path from paths has returned any token
if (nodesToKeep.Count == 0)
{
token.RemoveAll();
return token;
}
nodesToRemove.ExceptWith(nodesToKeep);
foreach (var notWhitelistedNode in nodesToRemove)
notWhitelistedNode.Remove();
return token;
}
private static void TraverseTokenPath(JToken value, ISet<JToken> nodesToRemove, ISet<JToken> nodesToKeep)
{
JToken? immediateValue = value;
do
{
nodesToKeep.Add(immediateValue);
if (immediateValue.Parent is JObject or JArray)
{
foreach (var child in immediateValue.Parent.Children())
if (!ReferenceEqualityComparer.Instance.Equals(child, value))
nodesToRemove.Add(child);
}
immediateValue = immediateValue.Parent;
} while (immediateValue != null);
}
}
For most cases this can be achieved by a simple single line extension method
public static string ToJson<T>(this T self, string path) => $#"{{""{path}"":{JObject.FromObject(self)[path]?.ToString(Formatting.None)}}}";
This is only valid for extracting an object nested under the root object but is easily adapted with a separate parameter to specify the output path if needed
Thanks to #dbc answer as a good solution, but like he said, it doesn't affect the performance. Sometimes the data loaded from database has numerous references and only ignoring ReferenceLoopHandling is not enough for serialization; hence the serialized data becomes very large and takes a lot of ram in server, and this is caused by repetition of serializing a single object. In this situation, it's better to make a limited jobject from data straightly, rather than making a jobject and then exclude the unwanted paths from it. This can be done with a little customization of database pure data and a ContractResolver. Let's assume all the database entities inherit from a class or interface like DbModel (this is necessary in this solution). Then by a special ContractResolver, serialization of objects can be limited. A sample is like below:
class TypeName
{
public Type Type { get; set; }
public string Name { get; set; }
}
class MyContractResolver : DefaultContractResolver
{
private List<List<TypeName>> allTypeNames = new List<List<TypeName>>();
public MyContractResolver(Type parentType, string[] includePaths)
{
foreach (var includePath in includePaths)
{
List<TypeName> typeNames = new List<TypeName>() { new TypeName() { Type = parentType } };
var pathChilderen = includePath.Split('.');
for(int i = 0; i < pathChilderen.Length; i++)
{
var propType = typeNames[i].Type.GetProperties().FirstOrDefault(c => c.Name == pathChilderen[i]).PropertyType;
if (propType.GetInterface(nameof(IEnumerable)) != null && propType != typeof(String))
{
propType = propType.GetGenericArguments().Single();
}
typeNames.Add(new TypeName() { Name = pathChilderen[i], Type = propType });
}
allTypeNames.Add(typeNames);
}
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
// only serializer properties that are in include paths
List<JsonProperty> excludeProperties = new List<JsonProperty>();
foreach (var property in properties)
{
if (typeof(DbModel).IsAssignableFrom(property.PropertyType) || (property.PropertyType.GetInterface(nameof(IEnumerable)) != null && property.PropertyType != typeof(String)))
{
Console.WriteLine(property.PropertyType.ToString());
var exclude = true;
foreach (var typeNames in allTypeNames)
{
var index = typeNames.FindIndex(c => c.Name == property.PropertyName && c.Type == property.PropertyType);
if (index > 0)
{
if (typeNames[index - 1].Type == type)
{
exclude = false;
goto EndSearch;
}
}
}
EndSearch:
if (exclude)
excludeProperties.Add(property);
}
}
properties = properties.Where(c => excludeProperties.All(d => d.PropertyName != c.PropertyName)).ToList();
return properties;
}
}
This class can be used like this:
// return Ok(data);
var jObject = JObject.FromObject(data,
JsonSerializer.CreateDefault(new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Converters = new List<JsonConverter>()
{
new ValidationProblemDetailsConverter(),
new ProblemDetailsConverter(),
new StringEnumConverter()
},
ContractResolver = new MyContractResolver(typeof(Foo), new[] { "bar", "baz.qux" })
}));
return Ok(jObject);
In this example Foo is the class of main object to return, and bar and baz are properties that are going to be serialized (they are loaded from database too). In addition qux is one of the baz properties that is loaded from database and has to be serialized. In this example all the other properties of each model that are not entities of database (so are not inherited from DbModel) are serialized and all the entities of database that exist in original data but not in the including paths, are ignored to be serialized.

SignalR 2.0.0 beta2 IJsonSerializer extensibility

I want to add some custom serialization logic so that the converted json contains camel case properties.
For that reason i tried to replace the default IJsonSerializer with one the i found in this link:
https://github.com/SignalR/SignalR/issues/500
However there seems to be a problem. More specifically, the JsonNetSerializer and IJsonSerializer classes do not exist in any of the signalR assemblies. Is there any change that happened to the recent version of signalR in that respect?
Just to clarify this a bit, as of SignalR 2 you can't replace the serializer with one that isn't from from JSON.NET. However, the JSON.NET serializer used by SinglR can be created and set using the DependacyResolver.
Here's an example where a new JsonSerializer is created to handle reference loops:
protected void Application_Start()
{
var serializerSettings = new JsonSerializerSettings();
serializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize;
serializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
var serializer = JsonSerializer.Create(serializerSettings);
GlobalHost.DependencyResolver.Register(typeof(JsonSerializer), () => serializer);
}
In SignalR 2.0 you can't replace the JsonSerializer, there's no more IJsonSerializer abstraction. It's always JSON.NET.
Here's an example of overriding the SignalR Dependency Resolver using StructureMap.
In this particular example, I'm converting to camelCase properties and converting enums as strings.
During startup:
Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver = new StructureMapSignalRDependencyResolver();
Here's the class:
public class StructureMapSignalRDependencyResolver : Microsoft.AspNet.SignalR.DefaultDependencyResolver
{
public override object GetService(Type serviceType)
{
object service;
if (!serviceType.IsAbstract && !serviceType.IsInterface && serviceType.IsClass)
{
// Concrete type resolution
service = StructureMap.ObjectFactory.GetInstance(serviceType);
}
else
{
// Other type resolution with base fallback
service = StructureMap.ObjectFactory.TryGetInstance(serviceType) ?? base.GetService(serviceType);
}
return service;
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var objects = StructureMap.ObjectFactory.GetAllInstances(serviceType).Cast<object>();
return objects.Concat(base.GetServices(serviceType));
}
}
And StructureMap was setup with:
ObjectFactory.Configure(c =>
{
c.Scan(a =>
{
// scan the assembly that SignalR is referenced by
a.AssemblyContainingType<AppHost>();
a.WithDefaultConventions();
});
c.For<Newtonsoft.Json.JsonSerializer>()
.Singleton()
.Use(new Newtonsoft.Json.JsonSerializer
{
ContractResolver = new SignalRContractResolver(),
Converters = { new Newtonsoft.Json.Converters.StringEnumConverter() }
});
});
Here is the Contract Resolver:
public class SignalRContractResolver : Newtonsoft.Json.Serialization.IContractResolver
{
private readonly Assembly _assembly;
private readonly Newtonsoft.Json.Serialization.IContractResolver _camelCaseContractResolver;
private readonly Newtonsoft.Json.Serialization.IContractResolver _defaultContractSerializer;
public SignalRContractResolver()
{
_defaultContractSerializer = new Newtonsoft.Json.Serialization.DefaultContractResolver();
_camelCaseContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
_assembly = typeof(Connection).Assembly;
}
public Newtonsoft.Json.Serialization.JsonContract ResolveContract(Type type)
{
if (type.Assembly.Equals(_assembly))
{
return _defaultContractSerializer.ResolveContract(type);
}
return _camelCaseContractResolver.ResolveContract(type);
}
}

Bind to action method

Is it possible to use a simple action method - just like with Caliburn.Micro - instead of a command with MvvmCross bindings?
Example:
public void Action()
{
Tip = 11;
}
<Button
android:text="Button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/button1"
local:MvxBind="Click Action" />
It doesn't work out of the box, I tested that.
While I found a lot of samples about adding new target bindings, I didn't find a single one about adding a new source binding.
UPDATE:
This works now out of the box with the Rio binding. To use it, add the MvvmCross MethodBinding NuGet package to the Android project.
Up until now, much of the emphasis for MvvmCross has been on allowing multi-platform target binding with the source remaining mainly 'vanilla' INotifyPropertyChanged.
There have been some deviation in terms of ViewModel structure - e.g.:
the MvxCommandCollection - http://slodge.blogspot.co.uk/2013/03/fixing-mvvm-commands-making-hot-tuna.html
some users using Fody - http://twincoders.com/blog/codigo-limpio-con-fody/
Recently, several new feature requests have also been logged in this area:
AutoCommands - I think this is what you are asking about here - https://github.com/slodge/MvvmCross/issues/301
Rio binding sources - https://github.com/slodge/MvvmCross/issues/299
Tibet binding - https://github.com/slodge/MvvmCross/issues/298
Because of these, I do expect more functionality to be exposed in this area in the future...
With that said, if you wanted to get this working today, then MvvmCross Binding is overrideable so you could fairly easily do it:
1. Implement an ICommand that invokes a MethodInfo using reflection (for completeness this should probably also use a parameter if available) - some kind of InvokeMethodCommand (code for this left to the reader!)
.
2. Implement an MyMethodSourceBinding class which wraps the InvokeMethodCommand - something like:
public class MyMethodSourceBinding : MvxSourceBinding
{
private readonly MethodInfo _methodInfo;
protected MyMethodSourceBinding(object source, MethodInfo methodInfo)
: base(source)
{
_methodInfo = _methodInfo;
}
public override void SetValue(object value)
{
// do nothing - not allowed
}
public override Type SourceType
{
get { return typeof(ICommand); }
}
public override bool TryGetValue(out object value)
{
value = new InvokeMethodCommand(source, _methodInfo);
return true;
}
}
3. Override MvvmCross's registered IMvxSourceBindingFactory with your own implementation that can detect when a method is present - sadly most of this is cut and paste coding today - it would be something like
public class MySourceBindingFactory
: IMvxSourceBindingFactory
{
private IMvxSourcePropertyPathParser _propertyPathParser;
private IMvxSourcePropertyPathParser SourcePropertyPathParser
{
get
{
if (_propertyPathParser == null)
{
_propertyPathParser = Mvx.Resolve<IMvxSourcePropertyPathParser>();
}
return _propertyPathParser;
}
}
public IMvxSourceBinding CreateBinding(object source, string combinedPropertyName)
{
var tokens = SourcePropertyPathParser.Parse(combinedPropertyName);
return CreateBinding(source, tokens);
}
public IMvxSourceBinding CreateBinding(object source, IList<MvxPropertyToken> tokens)
{
if (tokens == null || tokens.Count == 0)
{
throw new MvxException("empty token list passed to CreateBinding");
}
var currentToken = tokens[0];
if (tokens.Count == 1)
{
return CreateLeafBinding(source, currentToken);
}
else
{
var remainingTokens = tokens.Skip(1).ToList();
return CreateChainedBinding(source, currentToken, remainingTokens);
}
}
private static MvxChainedSourceBinding CreateChainedBinding(object source, MvxPropertyToken propertyToken,
List<MvxPropertyToken> remainingTokens)
{
if (propertyToken is MvxIndexerPropertyToken)
{
return new MvxIndexerChainedSourceBinding(source, (MvxIndexerPropertyToken) propertyToken,
remainingTokens);
}
else if (propertyToken is MvxPropertyNamePropertyToken)
{
return new MvxSimpleChainedSourceBinding(source, (MvxPropertyNamePropertyToken) propertyToken,
remainingTokens);
}
throw new MvxException("Unexpected property chaining - seen token type {0}",
propertyToken.GetType().FullName);
}
private static IMvxSourceBinding CreateLeafBinding(object source, MvxPropertyToken propertyToken)
{
if (propertyToken is MvxIndexerPropertyToken)
{
return new MvxIndexerLeafPropertyInfoSourceBinding(source, (MvxIndexerPropertyToken) propertyToken);
}
else if (propertyToken is MvxPropertyNamePropertyToken)
{
//**************************
// Special code is here
var propertyToken = (MvxPropertyNamePropertyToken) propertyToken;
if (source != null)
{
var method = source.GetType().GetMethod(propertyToken.PropertyName, BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance);
if (method != null)
{
return new MyMethodSourceBinding(source, method);
}
}
return new MvxSimpleLeafPropertyInfoSourceBinding(source,
(MvxPropertyNamePropertyToken) propertyToken);
// Special code ends here
//**************************
}
else if (propertyToken is MvxEmptyPropertyToken)
{
return new MvxDirectToSourceBinding(source);
}
throw new MvxException("Unexpected property source - seen token type {0}", propertyToken.GetType().FullName);
}
}
4. Supply this source binding factory in your own custom binding builder - e.g.:
public class MyAndroidBindingBuilder
: MvxAndroidBindingBuilder
{
protected override IMvxSourceBindingFactory CreateSourceBindingFactory()
{
return new MvxSourceBindingFactory();
}
}
5. Supply this binding builder during your setup
public class Setup : MvxAndroidSetup
{
// ....
protected override MvxAndroidBindingBuilder CreateBindingBuilder()
{
return new MyAndroidBindingBuilder();
}
}
Note: This approach is only for advanced users right now... As suggested in the first part of this question, I do expect the code in this area to change quite a lot so you might also encounter some issues maintaining a fork in this area. (Indeed the code in this area has already changed quite significantly on the Tibet Binding branch within the GitHub repo!)

How to modify linqtosql entityref objects in handcoded MVC model?

I am trying to set up my own mvc model rather than letting the environment create one via the graphic designer tool. I had hoped that this would make it easier to keep separate repositories for parts of the model space but so far it has caused me nothing but grief.
The first problem I ran into was that the entityref classes had to be updated via a selectlist control in the view. I managed to get that to work by adding an interal ID field to every entityref much like designer.cs would do. However, it has made the model class quite a bit more complex as the code below demonstrates.
Unfortunately, I now run into a problem when I want to explicitly update some of the entities in the controller. If I manually set the ID field, the update is just dropped, if I change the entity I get an exception while saving.
My model
[Table(Name = "dbo.Jobs")]
public class Job {
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int JobID { get; set; }
internal string _CompanyID; // string for legacy reasons
[Column(Storage = "_CompanyID")]
public string CompanyID{
get { return _CompanyID}
set {
if ((_CompanyID != value)) {
if (_MittlerOrg.HasLoadedOrAssignedValue) {
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}
_CompanyID = value;
}
}
}
internal EntityRef<Company> _Company;
[Association(Storage = "_Company", ThisKey = "CompanyID", OtherKey = "CompanyID", IsForeignKey = true)]
public Company Company {
get { return _Company.Entity; }
set {
Organization previousValue = this._Company.Entity;
if ((previousValue != value) || (_Company.HasLoadedOrAssignedValue == false)) {
if ((previousValue != null)) {
_Company.Entity = null;
}
_Company.Entity = value;
if (value != null) {
_CompanyID = value.OrganizationID;
} else {
_CompanyID = default(string);
}
}
}
}
// The contact depends on the choice of company and should be set
// inside an action method once the company is determined.
internal string _ContactID;
[Column(Storage = "_ContactID")]
public string ContactID {
get { return _ContactID; }
set {
if ((_ContactID != value)) {
if (_Contact.HasLoadedOrAssignedValue) {
throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException();
}
_ContactID = value;
}
}
}
internal EntityRef<User> _Contact;
[Association(Storage = "_Contact", ThisKey = "ContactID", OtherKey = "UserID", IsForeignKey = true)]
public User Contact {
get { return _Contact.Entity; }
set {
User previousValue = this._Contact.Entity;
if ((previousValue != value) || (_Contact.HasLoadedOrAssignedValue == false)) {
if ((previousValue != null)) {
_Contact.Entity = null;
}
_Contact.Entity = value;
if (value != null) {
_ContactID = value.UserID;
} else {
_ContactID = default(string);
}
}
}
}
}
The edit function that causes problems is here. If I step though it in the debugger I see that fi.ContactID gets updated but not committed to the DB.
[HttpPost]
public ActionResult Edit(int id, FormCollection collection) {
var user = userrep.FetchByLogin(User.Identity.Name);
var job = jobrep.FetchByID(id);
try {
var oldvalue = job.CompanyID;
UpdateModel(job, "job");
if (oldvalue != job.CompanyID) {
if (job.CompanyID != null) {
job.ContactID = orgrep.FetchByID(job.CompanyID).DefaultContactID;
} else {
job.ContactID = default(string);
}
}
firep.Save();
return RedirectToAction("Index");
} catch (Exception e) {
}
}
Any idea how to get those pesky entityrefs to behave? I searched up and down the internet but all model layer examples seem to cover the simplest relationships only. Should I just chuck the model completely in favor of managing my references manually though id fields.
Cheers,
Duffy
Update: I never got this piece of code to work robustly so I ended up switching back to letting visual studio generate the DataContext via the drag and drop graphical designer.
I still struggle a bit with fixing the names on the relationship links after an update of the db schema (I carefully name all relationships in the db but the designer tool seems to ignore those names) but since I discovered that the db.designer.cs file can be opened in an xml editor rather than with the GUI, the job got a lot easier.