Using Interface Derived Classes with NEST and ElasticSearch - c#

I'm trying to use NEST 2.X and Elastic Search for persistent storage of Users. Each User contains a list of Roles which define their permissions inside our platform.
There are several different types of Roles each deriving from a IMediaDetectionRole interface:
public class MediaDetectionUser
{
public string Username { get; set; }
public ICollection<IMediaDetectionRole> Roles { get; set; }
public MediaDetectionUser()
{
Roles = new List<IMediaDetectionRole>();
}
}
public interface IMediaDetectionRole
{
string Name { get; }
string GetDescription();
string GetRoleType {get;}
}
[ElasticsearchType(Name="MediaDetectionAdminRole")]
public class MediaDetectionAdminRole : IMediaDetectionRole
{
public string Name { get { return "Admin"; } }
public string GetDescription() { return "Admin users can create other users within the account"; }
public string GetRoleType { get { return this.GetType().Name; } }
}
[ElasticsearchType(Name = "MediaDetectionManagerRole")]
public class MediaDetectionManagerRole : IMediaDetectionRole
{
public string Name { get { return "Manager"; } }
public string GetDescription() { return "Managers can modify account-level properties"; }
public string GetRoleType { get { return this.GetType().Name; } }
}
[ElasticsearchType(Name = "MediaDetectionCreatorRole")]
public class MediaDetectionCreatorRole : IMediaDetectionRole
{
public string Name { get { return "Creator"; } }
public string GetDescription() { return "Creators can create new Media Detection Profiles"; }
public string GetRoleType { get { return this.GetType().Name; } }
}
I don't have any trouble storing the data in ElasticSearch, but when I go to query the data the data NEST can't figure out the type of these roles. I get:
Could not create an instance of type IMediaDetectionRole.
Type is an interface or abstract class and cannot be instantiated.
Path 'hits.hits[0]._source.roles[0].name', line 1, position 343.
What is the correct way to map nested object data back to the correct class type?
Many thanks!
-Z

Alright, so I guess this is less of a NEST/ES question and more of a JSON.NET question. Apparently the solution to this problem involves telling JSON.NET to provide type hints for each member of the list.
Per #MartijnLaarman's suggestion I added a [JsonProperty] attribute to the Roles property. See below:
public class MediaDetectionUser
{
public string Username { get; set; }
//This JsonProperty helps reference the types during deserialization
[JsonProperty("Roles", ItemTypeNameHandling = TypeNameHandling.All)]
public ICollection<IMediaDetectionRole> Roles { get; set; }
public MediaDetectionUser()
{
Roles = new List<IMediaDetectionRole>();
}
}
Here's an example of the JSON as it appears in the _source inside ElasticSearch:
Roles: [
{
$type: "MediaDetectionFrontend.ServiceModel.Types.MediaDetectionAdminRole, MediaDetectionFrontend.ServiceModel",
name: "Admin",
getRoleType: "MediaDetectionAdminRole"
},
{
$type: "MediaDetectionFrontend.ServiceModel.Types.MediaDetectionCreatorRole, MediaDetectionFrontend.ServiceModel",
name: "Creator",
getRoleType: "MediaDetectionCreatorRole"
},
{
$type: "MediaDetectionFrontend.ServiceModel.Types.MediaDetectionEditorRole, MediaDetectionFrontend.ServiceModel",
name: "Editor",
getRoleType: "MediaDetectionEditorRole"
}
]
You can see that the $type attribute now provides the full type descriptor of each element in the List.
Big thanks to #MartijnLaarman who helped me solve this issue even though it really has nothing to do with ElasticSearch and NEST.

Related

Return interface inherited classes in Generic method

I have 3 objects that are very similar with only a few differences
public class Person
{
public Person(ResourceObject resource)
{
// resource comes from an API provided by one
// of our systems (i have no control over it)
this.ResourceObject = resource;
}
// Resource
internal ResourceObject ResourceObject { get; }
// Similar properties
public string ObjectID { get; }
public string ObjectType { get; }
public IEnumerable<string> PropertyNames { get; }
// Person-specific property example - Organisation
public string Organisation { get; set; }
}
public class Computer
{
public Computer(ResourceObject resource)
{
// resource comes from an API provided by one
// of our systems (i have no control over it)
this.ResourceObject = resource;
}
// Resource
internal ResourceObject ResourceObject { get; }
// Similar properties
public string ObjectID { get; }
public string ObjectType { get; }
public IEnumerable<string> PropertyNames { get; }
// Computer-specific property example - OperatingSystem
public string OperatingSystem { get; set; }
}
public class Group
{
public Group(ResourceObject resource)
{
// resource comes from an API provided by one
// of our systems (i have no control over it)
this.ResourceObject = resource;
}
// Resource
internal ResourceObject ResourceObject { get; }
// Similar properties
public string ObjectID { get; }
public string ObjectType { get; }
public IEnumerable<string> PropertyNames { get; }
// Group-specific property example - Members
public string Members { get; set; }
}
I currently have GetPerson, GetComputer and GetGroup methods that are working but they essentially do the same thing and then call one of the specific object constructors. In an effort to dive into the world of Generics and Interfaces and learn more (as you do) i attempted to create a GetResource<T> method that would do the same job as those 3 methods without all the duplicate code.
I created the IResource Interface to identify common properties:
public interface IResource
{
string ObjectID { get; }
string ObjectType { get; }
IEnumerable<string> PropertyNames { get; }
}
and then attempted to create a GetResource<T> method but got stuck at the return code:
public static T GetResource<T>(string identity) where T : IResource
{
// get resource from system API
// and then return T somehow?
return new T(resourceObject);
}
I thought of changing the return value from T to IResource but i'm still not sure how i would identify which class to return (Perhaps i need a base class? Resource perhaps).
The reason i turned to Generics for this specific situation is if the system API updates and suddenly has a new Location object i don't want to have to create a GetLocation method and then have 4 methods that do exactly the same thing except for one line of code.
Is this the correct use case for Generics? and if so how can my method figure out what object to return?
Use a base class to hold common behavior.
public abstract class Resource {
protected Resource (ResourceObject resource) {
// resource comes from an API provided by one
// of our systems (i have no control over it)
this.ResourceObject = resource;
}
// Resource
internal ResourceObject ResourceObject { get; }
// Similar properties
public string ObjectID { get; }
public string ObjectType { get; }
public IEnumerable<string> PropertyNames { get; }
}
Derived classes
public class Person : Resource {
public Person(ResourceObject resource):base(resource){
}
// Person-specific property example - Organisation
public string Organisation { get; set; }
}
public class Computer : Resource {
public Computer(ResourceObject resource) : base(resource) {
}
// Computer-specific property example - OperatingSystem
public string OperatingSystem { get; set; }
}
public class Group : Resource {
public Group(ResourceObject resource) : base(resource) {
}
// Group-specific property example - Members
public string Members { get; set; }
}
Interfaces can't be initialized and thus trying to pass a constructor argument wont work.
With the base class constraint the generic method becomes
public static T GetResource<T>(string identity) where T : Resource {
// get resource from system API
// and then return T somehow?
return (T) Activator.CreateInstance(typeof(T), resourceObject);
}
And used
Person person = GetResource<Person>("person_identity");

C# Resolving Base Class to Correct Repository Method Call Based on Implemented Type

In our current project, we have an abstract base user class that is implemented by multiple other user types. We have a comparison class that can compare any of these types and then needs to call a correct update api based on the implemented type. I am trying to avoid bringing an if(typeof(User)) logic tree into the code and was hoping to figure out some way to solve the issue with method overloading. Are there any design patterns that can help solve this issue with some type of interface that can be dependency injected? Here is a basic code example
using System;
using System.Collections.Generic;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
List<BaseUser> TestUsers = new List<BaseUser>();
TestUsers.Add(new UserA() { Email = "test1#test.com", Location = "New York, NY" });
TestUsers.Add(new UserB() { Email = "test2#test.com", State = "TN" });
foreach (var user in TestUsers)
{
//need to invoke the correct Print repo method based on the actual user type, possible interface?
}
Console.Read();
}
}
public abstract class BaseUser
{
public string Email { get; set; }
}
public class UserA : BaseUser
{
public string Location { get; set; }
}
public class UserB : BaseUser
{
public string State { get; set; }
}
public class UserARepo
{
void Print(UserA user)
{
Console.Write($"User A Saved {user.Email}, {user.Location}");
}
}
public class UserBRepo
{
void Print(UserB user)
{
Console.Write($"User B Saved {user.Email}, {user.State}");
}
}
}
If the Print methods only need the Email and Location/State properties of the objects, then change their signature to accept only these two strings.
I would add a common property to the abstract class, which returns the needed property in the User* classes:
namespace ConsoleApplication5
{
public abstract class BaseUser
{
public string Email { get; set; }
public string OtherLocation { get; set;}
}
public class UserA : BaseUser
{
public string Location { get; set; }
public string OtherLocation {
get
{
return this.Location;
}
set
{
this.Location = value;
}
}
}
public class UserB : BaseUser
{
public string State { get; set; }
public string OtherLocation {
get
{
return this.State;
}
set
{
this.State = value;
}
}
}
public class UserARepo
{
void Print(BaseUser user)
{
Console.Write($"User A Saved {user.Email}, {user.OtherLocation}");
}
}
public class UserBRepo
{
void Print(BaseUser user)
{
Console.Write($"User B Saved {user.Email}, {user.OtherLocation}");
}
}
}
Hopefully I'm understanding the problem correctly - but if each of those UserTypes needs an update API or Print Repo method, put them as abstract methods in the class:
public abstract class BaseUser
{
public string Email { get; set; }
public abstract void PrintRepo();
}
Basically, it's just a question of: what functionality is required of the subclasses if they want to implement your base class? They have to handle Update requests? Then put an abstract declaration in the abstract class that says, "Hey, if you inherit from me, you have to include an Update function."
Then your main function becomes something like:
foreach (var user in TestUsers)
{
user.PrintRepo();
}

Returning an object derived from an interface with generic list

My application reads in JSON from disk and deserialising using JSON.net; which is working fine.
My JSON is laid out like this:
{
"driver": {
"driverTag": "blah_blah",
"driverName": "Blah Blah",
"driverTransport": "serial-device"
},
"devices": [
{
"deviceName": "Dev1",
"deviceTag": "DEV1",
"deviceStartMode": "Auto"
},
{
"deviceName": "Dev2",
"deviceTag": "DEV2",
"deviceStartMode": "Auto"
}
]
}
Based on the "driverTransport" value, I deserialise to either a SerialDriverConfig, TelnetDriverConfig, SNMPDriverConfig... etc class.
As the "driver" properties will be the same for every driver, no matter the transport type, I have a "DriverConfigTemplate" class. The "devices" will differ from JSON file to JSON file and have specific properties for that transport type (i.e. a serial device will have properties like "serialPortName", "serialBaudRate" etc.)
I have a "DriverConfig" interface, where T is "DeviceConfig".
public interface DriverConfig<T> where T : DeviceConfig
{
DriverConfigTemplate driver { get; set; }
List<T> devices { get; set; }
}
My device config is as follows:
public class DeviceConfig : IDeviceConfig
{
public string deviceTag { get; set; }
public string deviceName { get; set; }
public string deviceStartMode { get; set; }
}
Now; the problem part. When I am deserialising, I check the transport type before hand and determine the class to use; i.e for a serial driver I will use the "SerialDriverConfig" class and deserialise using the "SerialDeviceConfig":
public class SerialDeviceConfig : DeviceConfig
{
public int serialComPort { get; set; }
public int serialBaudRate { get; set; }
public int serialDataBits { get; set; }
public string serialParity { get; set; }
public string serialStopBits { get; set; }
public string serialHandshake { get; set; }
public int serialReadTimeout { get; set; }
public int serialWriteTimeout { get; set; }
public bool serialRtsEnable { get; set; }
public bool serialDtrEnable { get; set; }
}
My "SerialDriverConfig" class looks like this:
public class SerialDriverConfig : DriverConfig<SerialDeviceConfig>
{
public DriverConfigTemplate driver { get; set; }
public List<SerialDeviceConfig> devices { get; set; }
}
Again, this is fine and the JSON.net deserialiser does its job perfectly.
I have a function that gets called when the JSON config file has been loaded and validated against its respective schema, then passed on to a "DeserialiseDriverConfig" function where I am trying to return the derived driver object; which is where I am stuck :(
private DriverConfig<DeviceConfig> DeserialiseDriverConfig(string _json, string _driverTransport)
{
switch (_driverTransport)
{
case "serial-device":
try
{
SerialDriverConfig _serialDriverConfig = JsonConvert.DeserializeObject<SerialDriverConfig>(_json);
if (_serialDriverConfig != null)
{
return _serialDriverConfig;
}
}
catch (Exception e)
{
//Blah blah blah
}
break;
}
return null;
}
I have been stuck on this one for a few days, have tried many things and this is where I have ended up. I am getting "Cannot implicitly convert type "SerialDriverConfig" to "DriverConfig". An explicit conversion exists (are you missing a cast?)" So I understand why this error is occurring, but cannot get around it.
Hope my code makes sense and someone can help me out here?
You can change your DriverConfig class to be non-generic
public interface DriverConfig
{
DriverConfigTemplate driver { get; set; }
List<DeviceConfig> devices { get; set; }
}
and instead of using derived classes (SerialDriverConfig etc.) you can set Json.net to deserialize to the correct DeviceConfig type based on either having a $type attribute in your JSON like this or using a custom JsonConverter similar to this
I'm not sure if this solution fits your need but if you create your method and SerialDriverConfig with using generic type T you can use your interface as a returning type. Can you try the code below;
Your Method:
private static DriverConfig<T> DeserialiseDriverConfig<T>(string _json, string _driverTransport)
{
switch (_driverTransport)
{
case "serial-device":
try
{
SerialDriverConfig<T> _serialDriverConfig = JsonConvert.DeserializeObject<SerialDriverConfig<T>>(_json);
if (_serialDriverConfig != null)
{
return _serialDriverConfig;
}
}
catch (Exception e)
{
//Blah blah blah
}
break;
}
return null;
}
SerialDriverConfig Class:
public class SerialDriverConfig<T> : DriverConfig<T>
{
public DriverConfigTemplate driver { get; set; }
public List<T> devices { get; set; }
}
Also you should consider changing DriverConfig<T> interface approach because if you leave it as-is you will have boxing issue. If you do not need you may remove where T : DeviceConfig from your interface or modify it according to your current circumstances.
Hope this helps, please let me know if this works for you

Inheriting from an interface with properties of the same name

I have an interface IProduct and two partial classes SearchedProductInternal and SearchedProductExternal.
These two classes are extending classes coming from 3rd party webservice searches but both return slightly different result types.
I want to use the interface for both so they type returned is the same. I know how to inherit but what do I do to return the "Name" as both the interface and SearchedProductInternal have the same object name?
My Interface is similar to as follows:
public interface IProduct
{
string Name { get; }
string ID { get; }
string DescriptionShort { get; }
string DescriptionLong { get; }
}
My Object SearchedProductInternal has the following properties:
string Name;
int ObjectIdField;
string DescriptionShortField;
string DescriptionLongField;
So my this is where I am inheriting
public partial class SearchedProductInternal : IProduct
{
public string ID
{
get { return ObjectIdField.ToString(); }
}
public string Name
{
//What do I do here?
}
public string DescriptionShort{get { return shortDescriptionField; }
}
public string DescriptionLong {get { return longDescriptionField; }
}
}
I want to the return the name that has been originality assigned in the SearchedProductInternal class but I don't know how to do that because if I just put
return Name
I get a stackoverflow error as it appears to be just keeping calling its self?
I think what you should do here is to explicitly implement the interface, so that you will have both your Name property as defined in the class and the IProduct.Name property from your interface.
You can explicitly implement the interface, like so:
public partial class SearchedProductInternal : IProduct
{
string IProduct.ID
{
get { return ObjectIdField.ToString(); }
}
string IProduct.Name
{
get { return "Interface name"; }
}
string IProduct.DescriptionShort
{
get { return shortDescriptionField; }
}
string IProduct.DescriptionLong
{
get { return longDescriptionField; }
}
// Name property for the class, not the interface
public string Name
{
get { return "Class name"; }
}
}
This way you can differentiate between calls to your interface properties and properties with the same name on your class.
When accessing both properties you can also decide which you want, in the following manner:
var test = new SearchedProductInternal();
Console.WriteLine(test.Name); // returns "Class name"
Console.WriteLine((test as IProduct).Name); // returns "Interface name"
If your SearchedProductInternal already defines the property Name and you're trying to return the value of same Name property, you don't have to do anything.
Don't create one more property named Name. Just get rid of the Name property you added. Everything should work because the class already implemented the contract defined by the interface IProduct.
If you want to return different value from the IProduct.Name property, you can use explicit interface implementation.
You must change the name of the variable in this case Name.
If that was an ambigous sentence then remember it's the same for the PC. Name cannot be two things. but Name and _Name can.
public class SearchedProductInternal : IProduct
{
string _name = "test";
public string Name
{
get
{
return _name;
}
}
}
public interface IProduct
{
string Name { get; }
}
I agree with the above answer. But a minor issue here, we cannot expose the interface member as public, as it causes compile error.
We can have both class level and interface level members. The interface member cannot be accessed by using class instance, which can be accessed only through interface instance.
public interface IProduct
{
string Name { get; }
string ID { get; }
string DescriptionShort { get; }
string DescriptionLong { get; }
}
public partial class SearchedProductInternal : IProduct
{
private string _clsName;
private string _interfaceName;
private string _objectID;
private string _shortDesc;
private string _longDesc;
public SearchedProductInternal(string _cName, string _iName)
{
_clsName = _cName;
_interfaceName = _iName;
}
public string Name
{
get { return _clsName; }
}
string IProduct.Name
{
get { return _interfaceName; }
}
string IProduct.ID
{
get { return _objectID; }
}
string IProduct.DescriptionShort
{
get { return _shortDesc; }
}
string IProduct.DescriptionLong
{
get { return _longDesc; }
}
}
class Program
{
static void Main(string[] args)
{
SearchedProductInternal clsSearchProduct = new SearchedProductInternal("clsName", "interfaceName");
Console.WriteLine(clsSearchProduct.Name);
IProduct interfaceProduct = (IProduct)clsSearchProduct;
Console.WriteLine(interfaceProduct.Name);
Console.ReadLine();
}
}
I am not sure if I just explained this in a way that was not understood but the way that I got this to work was by just using {get;set;}
public partial class SearchedProductInternal : IProduct
{
public string ID
{
get { return ObjectIdField.ToString(); }
}
public string Name {get;set;}
public string DescriptionShort{get { return shortDescriptionField; }
}
public string DescriptionLong {get { return longDescriptionField; }
}
}

Neo4j: invalid arguments in CreateRelationship

I'm experimenting with the Neo4jClient in C# and am stuck at the following error:
The best overloaded method match for 'Neo4jClient.IGraphClient.CreateRelationship(Neo4jClient.NodeReference, GraphDB.PrecedesRelationshipo)' has some invalid arguments.
This error is for the line with the following code:
client.CreateRelationship<Process,PrecedesRelationship>(prevProcess, new PrecedesRelationship(currProcess, new PrecedesData(product, isOptional)));
Here, prevProcess and currProcess are both of type Neo4jClient.NodeReference. Actually, I generate the nodes and store their NodeReference values in a dictionary, so that I can easily look them up. The nodes are created just fine.
Below are my classes:
public class Process
{
public string Name { get; set; }
}
,
public class PrecedesData
{
public string Name { get; set; }
public bool IsOptional { get; set; }
public PrecedesData()
{ }
public PrecedesData(string name)
{
this.Name = name;
this.IsOptional = false;
}
public PrecedesData(string name, bool isOptional)
{
this.Name = name;
this.IsOptional = IsOptional;
}
}
and
public class PrecedesRelationship : Relationship<PrecedesData>, IRelationshipAllowingSourceNode<Process>,
IRelationshipAllowingTargetNode<Process>
{
public static readonly string TypeKey = "PRECEDES";
public PrecedesRelationship(NodeReference targetNode, PrecedesData data)
: base(targetNode, data)
{ }
public override string RelationshipTypeKey
{
get { return TypeKey; }
}
}
When I leave out the types in CreateRelationship I get the error that the compiler cannot infer the types.
I looked at the examples on the Neo4jClient Wiki and I thought I got it right but I seem to be mistaken.
What am I missing here?
You should be using Cypher, as the REST API is increasingly legacy. Really, anything non-Cypher is becoming legacy.
client.Cypher
.Start(new { prevProcess, currProcess })
.CreateUnique("prevProcess-[:PRECEDES {precedes}]->currProcess")
.WithParams(new { precedes = new PrecedesData(product, isOptional) })
.ExecuteWithoutResults();
Then, you don't need any relationship classes either.
Also, if you remove the excess constructors, you can shorten the entire code sample down to just this:
public class PrecedesData
{
public string Name { get; set; }
public bool IsOptional { get; set; }
}
client.Cypher
.Start(new { prevProcess, currProcess })
.CreateUnique("prevProcess-[:PRECEDES {precedes}]->currProcess")
.WithParams(new { precedes = new PrecedesData { Name = product, IsOptional = isOptional } })
.ExecuteWithoutResults();

Categories

Resources