Protobuf-net GRPC - System.NotSupportedException: 'Specified method is not supported.' - c#

I'm currently migrating a bunch of JSON Apis to gRPC. Once I call one of my service methods, I'm getting a "Specified method is not supported" exception with no further details. I suspect it might be something related to the nested generics, but I can't tell from which one therefore I have detailed them down below. Any help or guidance is greatly appreciated.
All operations have a common format defined as follows:
/// <summary>
/// A common class for results returned by the services. No data is returned here.
/// </summary>
[ProtoContract]
public class GrpcServiceResult
{
[ProtoMember(1)]
public bool Success { get; set; }
[ProtoMember(2)]
public string Message { get; set; } = null!;
public GrpcServiceResult() { }
public GrpcServiceResult(bool success, string message = "")
{
Success = success;
Message = message;
}
}
I originally had the class below inherit from the GrpcServiceResult above but I removed it since I suspected I might need to add ProtoInclude to it above, but the T is unknown therefore I can't do that:
/// <summary>
/// A common class for results returned by the services.
/// </summary>
/// <typeparam name="T">The <see cref="Data"/> type.</typeparam>
[ProtoContract]
public class GrpcServiceResult<T> where T : class
{
[ProtoMember(1)]
public bool Success { get; set; }
[ProtoMember(2)]
public string Message { get; set; } = null!;
[ProtoMember(3)]
public T Data { get; set; } = null!;
public GrpcServiceResult() { }
public GrpcServiceResult(bool success, T result = null!, string message = "")
{
Success = success;
Message = message;
Data = result;
}
}
In addition, some of the services return data that are paginated; thus, I defined another generic type to encapsulate the data:
[ProtoContract]
public class PagedResult<T> where T : class
{
[ProtoMember(1)]
public int CurrentPage { get; set; }
[ProtoMember(2)]
public int PageCount { get; set; }
[ProtoMember(3)]
public int PageSize { get; set; }
[ProtoMember(4)]
public int RowCount { get; set; }
[ProtoIgnore]
public int FirstRowOnPage
{
get { return (CurrentPage - 1) * PageSize + 1; }
}
[ProtoIgnore]
public int LastRowOnPage
{
get { return Math.Min(CurrentPage * PageSize, RowCount); }
}
[ProtoMember(5)]
public IList<T> Results { get; set; }
public PagedResult()
{
Results = new List<T>();
}
}
Finally, I have the following service with the following operation:
[ServiceContract]
public interface IDataReadService
{
// ...
/// <summary>
/// Get the data entry item structure.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
[OperationContract]
Task<GrpcServiceResult<PagedResult<SystemItemsResponseContract>>> GetSystemItems(SystemItemsRequestContract contract);
}
Regarding the request and response, they are defined as follows:
[ProtoContract]
public class SystemItemsRequestContract
{
[ProtoMember(1)]
public string SearchPattern { get; set; } = null!;
[ProtoMember(2)]
public int PartnerId { get; set; }
[ProtoMember(3)]
public int SearchField { get; set; }
[ProtoMember(4)]
public int Page { get; set; }
[ProtoMember(5)]
public int Count { get; set; }
}
And for the response (some fields with immutable types are removed for brevity):
[ProtoContract]
public class SystemItemsResponseContract : ItemInfoSyncResponseContract
{
[ProtoMember(1)]
public int Id { get; set; }
// I have built a custom surrogate for this one, and even if I remove it, I'm getting the same error
[ProtoMember(2)]
public DateTimeOffset? InsertedOn { get; set; }
//...
[ProtoMember(7)]
public ESellingType SoldAs { get; set; }
//...
// List of the same class
[ProtoMember(10)]
public List<SystemItemsResponseContract> Packs { get; set; }
}
[ProtoContract]
[ProtoInclude(1000, typeof(SystemItemsResponseContract))]
public class ItemInfoSyncResponseContract
{
//...
[ProtoMember(2)]
public List<ItemBarcode> Barcodes { get; set; } = null!;
//...
[ProtoMember(6)]
public List<string> Images { get; set; } = null!;
//... other properties are solely string, double, double?, int
}
[ProtoContract]
public class ItemBarcode
{
[ProtoMember(1)]
public string Barcode { get; set; } = null!;
[ProtoMember(2)]
public string Check { get; set; } = null!;
}
And this is the custom surrogate for the DateTimeOffset:
[ProtoContract]
public class DateTimeOffsetSurrogate
{
[ProtoMember(1)]
public long DateTimeTicks { get; set; }
[ProtoMember(2)]
public short OffsetMinutes { get; set; }
public static implicit operator DateTimeOffsetSurrogate(DateTimeOffset value)
{
return new DateTimeOffsetSurrogate
{
DateTimeTicks = value.Ticks,
OffsetMinutes = (short)value.Offset.TotalMinutes
};
}
public static implicit operator DateTimeOffset(DateTimeOffsetSurrogate value)
{
return new DateTimeOffset(value.DateTimeTicks, TimeSpan.FromMinutes(value.OffsetMinutes));
}
}
Sorry for the lengthy blocks of code but I'm currently not finding the issue. I'm running on these versions:
As for the call stack of the error:
at ProtoBuf.Grpc.Internal.Proxies.ClientBase.IDataReadService_Proxy_1.IDataReadService.GetSystemItems(SystemItemsRequestContract )
(No further information, the error appears to be thrown on the service call itself on the client side - not the server side).
Update
If I remove the parameter from the GetSystemItems function and use the following JSON surrogate to serialize the response (I'm communicating between 2 dotnet microservices), it will work:
[ProtoContract]
public class JsonSerializationSurrogate<T> where T : class
{
[ProtoMember(1)]
public byte[] ObjectData { get; set; } = null!;
[return: MaybeNull]
public static implicit operator T(JsonSerializationSurrogate<T> surrogate)
{
if (surrogate == null || surrogate.ObjectData == null) return null;
var encoding = new UTF8Encoding();
var json = encoding.GetString(surrogate.ObjectData);
return JsonSerializer.Deserialize<T>(json);
}
[return: MaybeNull]
public static implicit operator JsonSerializationSurrogate<T>(T obj)
{
if (obj == null) return null;
var encoding = new UTF8Encoding();
var bytes = encoding.GetBytes(JsonSerializer.Serialize(obj));
return new JsonSerializationSurrogate<T> { ObjectData = bytes };
}
}
Even if it does work for the response, I'd like to know what can be done to improve this since I'm sure this surrogate kind of defeats the purpose of gRPC.

The issue turned out from neither of those but rather from the way I initialized my client. Since I needed to add the surrogate, I created a new RuntimeTypeModel and a ClientFactory and passed those to the GRPC clients:
var runtimeTypeModel = RuntimeTypeModel.Create();
runtimeTypeModel.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
var binderConfig = BinderConfiguration.Create(new List<MarshallerFactory> {
ProtoBufMarshallerFactory.Create(runtimeTypeModel.Compile())
});
clientFactory = ClientFactory.Create(binderConfig);
services.AddSingleton(clientFactory);
var dataEntryChannel = GrpcChannel.ForAddress(GlobalSettings.Services.DataEntryEndpoint);
services.AddSingleton(_ => dataEntryChannel.CreateGrpcService<IDataReadService>(clientFactory));
I reused the default RuntimeTypeModel as follows:
RuntimeTypeModel.Default.Add(typeof(DateTimeOffset), false).SetSurrogate(typeof(DateTimeOffsetSurrogate));
var dataEntryChannel = GrpcChannel.ForAddress(GlobalSettings.Services.DataEntryEndpoint);
services.AddSingleton(_ => dataEntryChannel.CreateGrpcService<IDataReadService>());
You might ask why I did the first approach in the first place. I ran into issues previously that I though were from the RuntimeTypeModel not being passed on the GrpcChannel since some of my surrogates were not detected. Somehow it now works.

Related

Need help using generic type APIResponse class

My manager told me to use the following class while sending response back from Web pi controller in Asp.net core project, now I am new to programming and confuse how to make instance of it, i can convert it to JSON after. Only the usage of APIResponse class is out of my knowledge.
public class APIResponse<T>
{
public APIResponse()
: this(default(T))
{
}
public APIResponse(T data)
: this(ResponseCodes.RESPONSE_OK, ResponseMessages.RESPONSE_OK, data)
{
}
public APIResponse(int _code, string _msg, T _data)
{
this.code = _code;
this.msg = _msg;
this.data = _data;
}
public int code
{
get;
set;
}
public string msg
{
get;
set;
}
public T data
{
get;
set;
}
public int pageCount { get; set; }
public int pageSize { get; set; }
public int recordCount { get; set; }
}
I want to make object of it and add data in the fields.
Here is the response sample:
You could create a class called Data with the following fields:
public class Data
{
public string status;
public string statusMessage;
}
Then you would call the generic class and set the values like this,
APIResponse<Data> APIResponseData = new APIResponse<Data>();
APIResponseData.data.status = "Set Status";
APIResponseData.data.statusMessage = "Set Status Message";
APIResponseData.code = 1;
APIResponseData.msg = "message";
You can then use the APIResponseData object how you'd like.
Not sure if this is what you are asking for, but hope it helps you

c# Generic property

I am trying to make a class in C# that can be used to return data of any types.
public class ResponseObject
{
public <T> data { get;set }
public Boolean Success { get; set; }
public string Message { get; set; }
}
The Object will be a wrapper for the response object when my application sends a request to the API.
i have tried researching this but cannot find any tutorials which are relevant to what i am trying to do.
Is this possible in C#? the Response Object will be converted to a JSON string and then sent as a response.
I will not be doing any processing of this object as that will already by done. I just want to place the data inside the ResponseObject and send it
I want to do something along the lines of:
var customers = repository.GetCustomers();
var orders = repository.GetOrders();
if(customers)
{
success = true;
message = "";
}
else{
success = false;
message = "failed to get customers";
}
if(orders)
{
orderssuccess = true;
ordersmessage = "";
}
else{
orderssuccess = false;
ordersmessage = "failed to get orders";
}
ResponseObject customerResponse = new ResponseObject{
data = customers,
success = success,
message = message
};
ResponseObject orderResponse = new ResponseObject{
data = orders,
success = orderssuccess,
message = ordersmessage
};
You need to add <T> to the class and use T as the type of your property.
public class ResponseObject<T>
{
public T data { get; set; }
public Boolean Success { get; set; }
public string Message { get; set; }
}
You have almost done it already! Just change your <T> to T.
public class ResponseObject<T> where T : class
{
public T data { get; set; }
public Boolean Success { get; set; }
public string Message { get; set; }
}
Here where T : class ensure that the generic type parameter is a reference type. From your question it seems you are going to pass in an object there.
You have two options here:
Make the class generic, or
Use generic methods for accessing the property
Here are the examples of both approaches:
// Make the class generic
public class ResponseObject<T> {
public T Data { get; set }
public Boolean Success { get; set; }
public string Message { get; set; }
}
// Use generic methods to access the property
public class ResponseObject {
private object data;
public T GetData<T>() {
return (T)data;
}
public void SetData<T>(T newData) {
data = newData;
}
public Boolean Success { get; set; }
public string Message { get; set; }
}
Second approach is less robust than the first one - basically, it's a glorified unrestricted cast. First approach, however, does not let you build a container of ResponseObjects with different Ts. You can address this problem by adding an interface on top of ResponseObject:
interface IResponseObject {
object DataObj { get; set }
Type ObjType { get; }
bool Success { get; set; }
string Message { get; set; }
}
public class ResponseObject<T> {
public T Data { get; set }
public ObjType => typeof(T);
public DataObj {
get => Data;
set => Data = value; // C# 7 syntax
}
public Boolean Success { get; set; }
public string Message { get; set; }
}

C#. Universal desition for storing different types

I'm looking for the best approach of working with different types identically.
I have a web service that goes to specific resource, makes some research and returns an object WebResult, that contains all information about completed operations.
And now I'd like to build a set of different metrics, that will describe all received results. These metrics should provide
different types of data
easy way to collect it
possibility to deserialize it.
Example 1
First I've created separate classes for different metrics
public abstract class AbstractStatistic
{
public string Url { get; set; }
public string ExceptionMessage { get; set; }
public abstract void FillAllMetrics(WebResult result);
}
public class Resource1Statistic : AbstractStatistic
{
public string Title { get; set; }
public string[] Table1_Header { get; set; }
public int Table1_RowCount { get; set; }
public string[] Table2_Header { get; set; }
public int Table2_RowCount { get; set; }
public override void FillAllMetrics(WebResult result)
{
this.Url = result.url;
this.Title = result.data["title"];
this.Table1_Header = result.data["table1.header"].ToObject<string[]>();
//...
}
}
It works, but I'd like to make it in more standard way. One of the reason is that in this approach I have to create separate web form for each metrics.
Example 2
Second working example is universal but redundant: create an abstraction of any datatype
public abstract class AbstractStatistic
{
public string Url { get; set; }
public string Exception { get; set; }
public Dictionary<string, Metric> Metrics { get ;set;}
public abstract void FillAllMetrics(WebResult webResult);
}
public class Metric // Specific class for data
{
public string StringValue { get; set; }
public int? IntegerValue { get; set; }
public string[] ArrayValue { get; set; }
public DateTime? DateTimeValue { get; set; }
}
public class Resource1Statistic : AbstractStatistic
{
public override void FillAllMetrics(WebResult result)
{
this.Metrics.Add("title",
new Metric() { StringValue = result.data["title"].ToString() });
this.Metrics.Add("Table1 Header",
new Metric() { ArrayValue = result.data["table1.header"].ToObject<string[]>() });
//...
}
It works, but I'm sure there is more elegant solution. I don't like to take all these null values in json.
Examples 3
Generic solution (regarding to Adwaenyth)
public abstract class AbstractStatistic
{
public string Url { get; set; }
public string Exception { get; set; }
public List<AbstractMetric> Metrics { get ;set;}
public abstract void FillAllMetrics(WebResult webResult);
}
public abstract class AbstractMetric{}
public class Metric<T> : AbstractMetric
{
public string Name { get; set; }
public T Value { get; set; }
public string Type { get; private set; }
public Metric()
{
this.Type = typeof(T).ToString();
}
}
public class Resource1Statistic : AbstractStatistic
{
public override void FillAllMetrics(WebResult result)
{
this.Metrics.Add(new Metric<string>()
{ Name = "title",
Value = result.data["title"].ToString() });
this.Metrics.Add(new Metric<string[]>()
{ Name = "Table1 Header",
Value = result.data["table1.header"].ToObject<string[]>() });
//...
}
This solution looks nice, but I have to write custom deserializer.
What do you think, is there some good pattern that fits to my task? Or what's the best approach?

Trying to update an entity using EF and send it using WCF - property is causing an exception in an update scenario

I'm trying to send an object using WCF. The object is retrieved from the DB using EF.
This is the exception I get:
This only happens in an update scenario. Insert works perfectly.
Tracking the bug, I found that the problem is with a collection (called Travelers) I added recently.
Here is what happens when I try to watch its value at runtime, after updating, before sending the updated entity by WCF:
Here's the offending class' property declaration (I tried uncommenting the DataMember attribute but it didn't work):
[DataContract]
public class Travel : InsuredObject, ISaleEntity, ICloneable
{
//[DataMember]
public virtual ICollection<Traveler> Travelers { get; set; }
...
I've read that this.Configuration.ProxyCreationEnabled = false; and/or this.Configuration.LazyLoadingEnabled = false; might fix it, but I can't change those for reasons beyond me, and even when I tried playing with them - I got some other exceptions...
Additional Code:
The update method:
public virtual TEntity CreateAndUpdate(int saleId, TEntity entity) {
var context = ((IObjectContextAdapter)this.Context).ObjectContext;
var objBaseSet = context.CreateObjectSet<TBase>();
var entityBaseKey = context.CreateEntityKey(objBaseSet.EntitySet.Name, entity);
Object foundBaseEntity;
var baseExists = context.TryGetObjectByKey(entityBaseKey, out foundBaseEntity);
entity.Id = saleId;
if (!baseExists) {
this.GetDbSet<TEntity>().Add(entity);
}
this.objectContext.SaveChanges();
return entity;
}
Retrieving the containing object before updating:
public virtual IQueryable<TEntity> GetAll(Expression<Func<TEntity, bool>> where, bool brutalRefresh = false) {
IQueryable<TEntity> retObj = this.GetDbSet<TEntity>();
if (where != null) {
retObj = retObj.Where(where);
}
if (brutalRefresh) {
var context = ((IObjectContextAdapter)this.Context).ObjectContext;
context.Refresh(RefreshMode.StoreWins, retObj);
}
return retObj;
}
...All that code is common code with other projects, that send and receive the same entity as I do, it's just the Travel entity I added, that causes me problems, so the solution I'm looking for should consist of 0 changes in common code..
Traveler Class(fully):
[DataContract]
public class Traveler : ISaleEntity, ICloneable
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public string IDNumber { get; set; }
[DataMember]
public DateTime? BirthDate { get; set; }
[DataMember]
public virtual ICollection<SelectedCoverage> SelectedCoverages { get; set; }
[NotMapped]
public List<MedicalQuestionnaireAnswer> MedicalQuestionnaireAnswers
{
get
{
if (string.IsNullOrWhiteSpace(DBMedicalQuestionnaireAnswers))
return new List<MedicalQuestionnaireAnswer>();
return DBMedicalQuestionnaireAnswers.Split(',')
.Select(c => (MedicalQuestionnaireAnswer)int.Parse(c)).ToList();
}
set { DBMedicalQuestionnaireAnswers = string.Join(",", value.Select(m => (int)m)); }
}
[NotMapped]
public Genders Gender
{
get { return (Genders)DBGender; }
set { DBGender = (int)value; }
}
/// <summary>
/// NOTE! Do not use this property directly! use MedicalQuestionnaireAnswers instead
/// </summary>
[DataMember]
public string DBMedicalQuestionnaireAnswers { get; set; }
/// <summary>
/// NOTE! Do not use this property directly! use Gender instead
/// </summary>
[DataMember]
public int DBGender { get; set; }
public object Clone()
{
Traveler traveler = new Traveler();
traveler.FirstName = this.FirstName;
traveler.LastName = this.LastName;
traveler.IDNumber = this.IDNumber;
traveler.BirthDate = this.BirthDate;
traveler.DBMedicalQuestionnaireAnswers = this.DBMedicalQuestionnaireAnswers;
traveler.Gender = this.Gender;
if (this.SelectedCoverages != null)
{
traveler.SelectedCoverages = this.SelectedCoverages.Select(sc => (SelectedCoverage)sc.Clone()).ToList();
}
return traveler;
}
}
public static class TravelerExtension
{
/// <summary>
/// copy all the property except from the id and src defualt values
/// </summary>
/// <param name="dbTraveler"></param>
/// <param name="src"></param>
public static void CopyTravelerProperties(this Traveler target, Traveler src)
{
target.FirstName = src.FirstName;
target.LastName = src.LastName;
target.IDNumber = src.IDNumber;
target.BirthDate = src.BirthDate;
target.DBMedicalQuestionnaireAnswers = src.DBMedicalQuestionnaireAnswers;
target.DBGender = src.DBGender;
target.SelectedCoverages.CopySelectedCoveragesProperties(src.SelectedCoverages);
}
}
public static class TravelersExtension
{
/// <summary>
/// copy all the property except from the id and src defualt values
/// </summary>
/// <param name="dbTravelers"></param>
/// <param name="src"></param>
public static void CopyTravelersProperties(this ICollection<Traveler> target, ICollection<Traveler> src)
{
List<int> allTravelersIdsSrc = src.Select(t => t.Id).ToList();
// remove ids exist target and not in src
target.ToList().RemoveAll(t => allTravelersIdsSrc.Contains(t.Id));
target = target ?? new List<Traveler>();
foreach (Traveler srcTraveler in src)
{
var targetTraveler = target.FirstOrDefault(targetTrv => srcTraveler.Id != 0 && targetTrv.Id == srcTraveler.Id);
// if not exist traveler with target traveler id in db
if (targetTraveler == null)
{
// add srcTraveler to target
target.Add(srcTraveler);
}
else
{
targetTraveler.CopyTravelerProperties(srcTraveler);
}
}
}
}
Further info:
The immediate window exception does not occur if calling ToList() prior to trying to get the value in the immediate window. The problem itself persists though.
Trying to comment the [DataMember] attribute on:
public virtual ICollection<SelectedCoverage> SelectedCoverages { get; set; }
in Traveler class had no impact.
The exception:
Further info 2:
There is just 1 entity that causes the exception:
public class Quote : ISaleEntity, ICloneable {
...
[DataMember]
public virtual Travel Travel { get; set; }
...
When i change the above [DataMember] to [IgnoreDataMember] - no exception.
I set all the properties of this class to [IgnoreDataMember]
[DataContract]
public class Travel : InsuredObject, ISaleEntity, ICloneable
{
[IgnoreDataMember]
//[DataMember]
public bool? IsFromIsrael { get; set; }
[IgnoreDataMember]
//[DataMember]
public virtual ICollection<Traveler> Travelers { get; set; }
[IgnoreDataMember]
public virtual Quote Quote { get; set; }
[IgnoreDataMember]
//[DataMember]
[NotMapped]
public List<int> DestinationsCodes
{
get
{
if (string.IsNullOrWhiteSpace(DBDestinationsCodes))
return new List<int>();
return DBDestinationsCodes.Split(',').Select(c => int.Parse(c)).ToList();
}
set { DBDestinationsCodes = string.Join(",", value); }
}
/// <summary>
/// NOTE! Do not use this property directly! use DestinationsCodes instead
/// </summary>
[IgnoreDataMember]
//[DataMember]
public string DBDestinationsCodes { get; set; }
...
But the exception still occurs. Probably because of the class this class inherits from:
[DataContract]
[KnownType(typeof(Vehicle))]
[KnownType(typeof(Apartment))]
[KnownType(typeof(Travel))]
public class InsuredObject : ISaleEntity, ICloneable {
[Key]
[DataMember]
public int Id { get; set; }
[DataMember]
public int? OwnerTypeId { get; set; }
//navigation property
[DataMember]
public bool? HasShiabud { get; set; }
[DataMember]
[IgnoreDataMember]
public virtual Shiabud Shiabud { get; set; }
//[NotMapped]
//public virtual Proposal Proposal { get; set; }
//[DataMember]
[IgnoreDataMember]
public virtual ICollection<Coverage> Coverages { get; set; }
...
So how can I send this entity via WCF?
By making your properties Virtual, what you are doing is lazy loading.
You can make properties into ICollection instead, and do eager loading instead.
Can you share the part of your code with the EF context that does the DB update?
From the error message I gather that it is not WCF that is playing tricks. Rather, it appears that the EF ObjectContent has been disposed by the time you are applying your changes (the SaveChanges() method). This can happen if you keep it alive between WCF method calls, e.g. having it as a static variable in your backend and having it in a using-clause.
This should work to apply lazy-loading to your DB context for EF 6 and newer, though it works similarly for older versions. (I implemented a inherited class to help deal with the configurations. This may not be required)
public class DomainDbContext : DbContext
{
Configuration.LazyLoadingEnabled = false;
}

How to go about combining two objects, manipulate the data, then separate them?

I have two classes with some similar fields, some different, and a form that utilizes two different objects depending on what mode it's in (insert/edit).
Instead of using two different objects and if statements checking the form mode, I'd like to have one struct to be hydrated with either of the two objects fields so I can manipulate one object through the page life-cycle. Then separated the struct back to its respective object for insert/updating the DB.
Example of classes:
public partial class SomeClass
{
public Int32 B {get;set;}
public String C {get;set;}
public Boolean D {get;set;}
}
public class SomeOtherClass
{
public Int32 A {get;set;}
public Int32 B {get;set;}
public String C {get;set;}
}
Update with Solution Example:
public interface IInsertable
{
string SharedName { get; set; }
string SharedID { get; set; }
string editedFieldValue { get; set; }
long GetSuperSecreteInfo();
}
internal class InsertableImplementation : IInsertable
{
public string SharedName { get; set; }
public string SharedID { get; set; }
public string editedFieldValue { get; set; }
public long GetSuperSecreteInfo()
{
return -1;
}
}
public interface IUpdateable
{
string SharedName { get; set; }
string SharedID { get; set; }
string updatedFieldValue { get; set; }
Guid GenerateStevesMagicGuid();
}
internal class UpdateableImplementation : IUpdateable
{
public string SharedName { get; set; }
public string SharedID { get; set; }
public string updatedFieldValue { get; set; }
public Guid GenerateStevesMagicGuid()
{
return new Guid();
}
}
public static class WonderTwinFactory
{
public static WonderTwins GenerateWonderTwin(IUpdateable updateable, IInsertable insertable)
{
var wt = new WonderTwins();
// who will win?
wt.SharedID = updateable.SharedID;
wt.SharedID = insertable.SharedID;
// you decide?
wt.SharedName = updateable.SharedName;
wt.editedFieldValue = "stuff";
return wt;
}
}
public class WonderTwins : IInsertable, IUpdateable
{
public string SharedName { get; set; }
public string SharedID { get; set; }
public string editedFieldValue { get; set; }
public long GetSuperSecreteInfo()
{
return 1;
}
public string updatedFieldValue { get; set; }
public Guid GenerateStevesMagicGuid()
{
return new Guid();
}
}
class Program
{
static void Main(string[] args)
{
IUpdateable updateable = new UpdateableImplementation();
IInsertable insertable = new InsertableImplementation();
WonderTwins dualImplementatin = WonderTwinFactory.GenerateWonderTwin(updateable, insertable);
IUpdateable newUpdateable = dualImplementatin as IUpdateable;
IInsertable newInsertable = dualImplementatin as IInsertable;
}
}
Have both classes implement an interface that defines the operations common to each, including both the fields that are shared (assuming the view needs to access them) and also a method to actually perform the operation that they represent (insert/edit).
Other way of doing such things is using C# dynamic object and assign properties directly. It may help to avoid any new type or interface and directly utilizing new dynamic object any time, as much as required.
var newObject = new {
objectOfClass1 = x.prop1,
objectOfClass2 = x.prop2
}

Categories