C# GRPC Reference existing object - c#

Im completely new in using GRPC. I have and question regarding setting up the .proto file.
In my existing solution i have forexample this class:
public class Car
{
public int Id { get; set; }
public string Brand { get; set; }
public string? Type {get; set;}
}
The Car class is placed in a Core project, since it's used by other logic around the solution. But if i like to return in my GRPC Server, is it really necessary to define it in the .proto file again ala this:
message CarReply {
int32 Id = 1;
string Brand = 2;
string Type = 3;
}
What i liked was an reference to my Car() class. Is this not possible?

If you want to use vanilla "contract first" protobuf, then yes: you need to use a schema and the generated type - it is the generated type that knows how to perform the serialization.
However! There may be an alternative; protobuf-net (and protobuf-net.Grpc) provide an independent implementation of protobuf that supports "code first" usage, which allows you to use your existing type model.
The easiest way to do this with protobuf-net is to annotate your model (using either protobuf-net's attributes, or the framework data-contract attributes - the first option giving more control); for example:
[ProtoContract]
public class Car
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Brand { get; set; }
[ProtoMember(3)]
public string? Type {get; set;}
}
It is also possible to configure everything at runtime, but that's a bit harder.
This can also be used fully with gRPC, using interfaces to define the service contract; full guidance is here

Related

FileHelpers error: The field: 'k__BackingField' has the type: XXX that is not a system type, so this field need a CustomConverter

I need to read a CSV file with FileHelpers based on type, automatically generated by my MVC model. The model looks like this:
public partial class Merchant
{
public long Id { get; set; }
public string Name { get; set; }
public Nullable<int> Category { get; set; }
public virtual MerchantCategory MerchantCategory { get; set; }
}
The last field is obviously generated by a foreign key in database, referring to table MerchantCategories.
Then I attempt to create an instance of FileHelperEngine with this type:
var engine = new FileHelperEngine<Merchant>();
And get the following exception:
The field: 'k__BackingField' has the type: MerchantCategory that is not a system type, so this field need a CustomConverter ( Please Check the docs for more Info).
Actually I don't need this field at all for my import, so I tried to ignore it in derived class:
[DelimitedRecord(",")]
public class MerchantForImport : Merchant {
[FieldHidden]
new public MerchantCategory MerchantCategory;
}
var engine = new FileHelperEngine<MerchantForImport>();
And still the same error. I don't need this field at all, I don't want to implement any FieldConverter for it, I never asked for this k__BackingField and it's nowhere to be found in my code!
I can't call FileHelperEngine.Options.RemoveField() because the exception is thrown by the constructor.
Where does that come from? How do I get rid of it?
From a design perspective, I think you are going about it the wrong way. You are trying to use the Merchant class for two incompatible uses. Instead you should have two separate classes.
FileHelpers is a library for describing csv files so that you can import them easily. You should have a MerchantFileSpec for describing your file. It's really not a proper C# class - it may have: dummy fields to represent unused columns; lots of attributes [FieldNullValue], [FieldQuoted], [FieldConverter]; etc. It works best with public fields (a FileHelpers limitation which is not C# best practice), etc. It is a convenience syntax for describing the import file. It should not include any business logic or special constructors, or backing fields. Keep it as simple as possible.
Then you can have your MVC-generated Merchant class which is separate. Its purpose is to describe the merchant as required by the MVC framework, with foreign keys, ids, whatever.
Then you use a FileHelperEngine<MerchantFileSpec> to read the records into an array and map it to an enumerable of Merchant (via Linq or a library like AutoMapper).
Something like:
/// Your MVC-generated class. Add methods, getters, setters, whatever.
/// FileHelpers doesn't use this class.
class Merchant
{
public long Id { get; set; }
public string Name { get; set; }
public Nullable<int> Category { get; set; }
public virtual MerchantCategory MerchantCategory { get; set; }
}
/// This is the class FileHelpers will use
/// This class describes the CSV file only. Stick to whatever
/// syntax conventions are required by FileHelpers.
[DelimitedRecord(";")]
class ProductMerchantFileSpec
{
[FieldQuoted(QuoteMode.OptionalForRead)]
public long Id;
[FieldQuoted(QuoteMode.OptionalForRead)]
public string Name;
[FieldQuoted(QuoteMode.OptionalForRead)]
// Handle non-US formats such as , decimal points
// convert from inches to centimetres?
// you get the idea...
[FieldConverter(MyCustomizedCategoryConverter)] // you get the idea
public int Category;
}
class Program
{
static void Main(string[] args)
{
var engine = new FileHelperEngine<ProductMerchantFileSpec>();
var productMerchantRecords = engine.ReadFile(filePath);
var productMerchants = productMerchantRecords
.Select(x => new Merchant() { Id = x.Id, Name = x.Name, Category = x.Category });
}
}
I received this error specifically because my object (i.e. Merchant) was missing a column that existed in the source file. I was able to work around the issue prior to realizing the missing column by adding a new property to my object class public string[] MyProperty { get; set; }. This work-around help me realize a column was missing.
i.e..
public partial class Merchant
{
public long id { get; set; }
..
..
..
public string[] MyProperty { get; set; }
}

How to get a reference to the parent object in a deserialized MongoDB document?

I hope someone can help. I'm getting to grips with the C# driver for MongoDB and how it handles serialization. Consider the following example classes:
class Thing
{
[BsonId]
public Guid Thing_ID { get; set; }
public string ThingName {get; set; }
public SubThing ST { get; set; }
public Thing()
{
Thing_ID = Guid.NewGuid();
}
}
class SubThing
{
[BsonId]
public Guid SubThing_ID { get; set; }
public string SubThingName { get; set; }
[BsonIgnore]
public Thing ParentThing { get; set; }
public SubThing()
{
SubThing_ID = Guid.NewGuid();
}
}
Note that SubThing has a property that references its parent. So when creating objects I do so like this:
Thing T = new Thing();
T.ThingName = "My thing";
SubThing ST = new SubThing();
ST.SubThingName = "My Subthing";
T.ST = ST;
ST.ParentThing = T;
The ParentThing property is set to BsonIgnore because otherwise it would cause a circular reference when serialising to MongoDB.
When I do serialize to MongoDB it creates the document exactly how I expect it to be:
{
"_id" : LUUID("9d78bc5c-2abd-cb47-9478-012f9234e083"),
"ThingName" : "My thing",
"ST" : {
"_id" : LUUID("656f17ce-c066-854d-82fd-0b7249c80ef0"),
"SubThingName" : "My Subthing"
}
Here is the problem: When I deserialize I loose SubThing's reference to its parent. Is there any way to configure deserialization so that the ParentThing property is always its parent document?
from mongodb web site
Implementing ISupportInitialize -
The driver respects an entity implementing ISupportInitialize which contains 2 methods, BeginInit and EndInit. These method are called before deserialization begins and after it is complete. It is useful for running operations before or after deserialization such as handling schema changes are pre-calculating some expensive operations.
so, Thing will implement ISupportInitialize and the function BeginInit will stay empty and Endinit will contain St.ParentThing = this;
At this level of abstraction, it's hard to give a definite answer.
One way is to have the class implement ISupportInitialize which offers a hook after de-serialization. That is probably the easiest solution for the problem at hand. Otherwise, the same link also shows how to write a custom serializer, but that shouldn't be required in this case.

How to hydrate an object with values taken from struct in C#

Is there in C# any hydrating technique allowing to transfer values from one struct/object to another struct/object if they have similar fields or based on certain strategy. I came from Zend Framework 2 world, and it provides the feature "Hydrator" which allows do exactly what I said above. So, I am wondering whether Asp.Net or C# provides something similar.
To make it clear, I want something like this:
struct UserInfo {
public string FirstName { get; set; };
public string LastName { get; set; };
public int Age { get; set; };
}
class UserUpdateModel {
public string FirstName { get; set; };
public string LastName { get; set; };
public int Age { get; set; };
}
...
//supposed UserUpdateModel model I is gotten from the action param
UserInfo info = new UserInfo();
Hydrator hydrator = new Hydrator(Hydrator.Properties);
hydrator.hydrate(info, model);
Now, "info" should be populated with values from "model"
Any help is appreciated.
Yes. AutoMapper. It is designed specifically for this. I personally prefer writing ViewModel constructor that takes an entity and copies the properties. I like the control and familiarity of good old C# code even if it takes a bit more effort.
Automapper should do the trick. You can use it as a nuget package.
Once you have your types and a reference to AutoMapper, you can create a map for the two types.
Mapper.CreateMap<UserUpdateModel, UserInfo>();
The type on the left is the source type, and the type on the right is the destination type. To perform a mapping, use the Map method.
UserInfo info = Mapper.Map<UserInfo>(userUpdateModel);

How do you use BsonClassMap to map a POCO domain object property as a manual or DBRef reference?

Using BsonClassMap, is it possible to map a domain object reference while keeping the domain object assembly persistent ignorant (changing the public A Reference { get; set; } property to public MongoDBRef Reference{ get; set; } in the sample class B below is not acceptable).
For this case, the referenced object is not a part of the same aggregate, and should not be stored as a nested document.
Is it possible map two domain objects in a relationship like this:
public class A
{
public Guid Id {get; private set; }
}
public class B
{
public Guid Id { get; private set; }
public A Reference { get; set; }
}
Into the following document structure:
// Collection for class A
{ _id: "11111111-1111-1111-1111-111111111111" }
// Collection class B
{
_id: "22222222-2222-2222-2222-222222222222",
reference_id: "11111111-1111-1111-1111-111111111111"
}
The mapping may look like:
BsonClassMap.RegisterClassMap<A>(cm =>
{
cm.MapIdProperty(c => c.Id)
.SetIdGenerator(new GuidGenerator())
.SetRepresentation(BsonType.String);
}
BsonClassMap.RegisterClassMap<B>(cm =>
{
cm.MapIdProperty(c => c.Id)
.SetIdGenerator(new GuidGenerator())
.SetRepresentation(BsonType.String);
// How do I map the B.Reference to a manual reference (like
// the sample doc structure above) or possibly as a DBRef?
}
So, without changing the model, how do I map the Reference property to object A, from object B as either a DBRef or as a manual references (as in my sample document structure above)?
Is this possible using BsonClassMap? Or in order to use BsonClassMap and keep my domain assembly persistent ignorant, do I need to change the model to something like:
public class A
{
public Guid Id {get; private set; }
}
public class B
{
public Guid Id { get; private set; }
public Guid ReferenceId { get; set; } // Don't reference the object directly,
// just store the Guid to the
// referenced object.
}
I posed this same question to the mongodb-csharp user group and got a response from craiggwilson:
You'll need to change your ReferenceProperty to ReferencePropertyId. We do not support lazy-loading (or eager-loading) of referenced documents.
Since A is not the aggregate for B, then this actually makes more sense when discussing in these terms. Generally, it is unnecessary for a referenced aggregate (B) to be loaded in order to process the referencing aggregate (A). It might be that you do indeed need some information from B. In this case, think about denormalizing a little and creating a true entity (BSummary) whose aggregate is A. This would make sense if some of the summary information is immutable or changes infrequently.

Naming conventions for DTOs if C# and JavaScript Apps are talking to each other

The naming conventions for a TypeScript class entity say that I should use camelCase for my attribute names in TypeScript.
So like:
export class Bird {
type: string;
nameOfBird: string;
}
But the naming conventions in C# say that I should prefix class attributes with an _:
public class Bird {
public string _type {get; set;}
public string _nameOfBird {get; set;}
}
But when sending those as JSON between my apps, I get a conflict, because I do not know whether I should use the camelCase or _case in my JSON object. And it also seems to make the marshalling harder.
How do you handle this? Just ignore one of the guidelines or do the marshalling between both?
You can achieve both - by just using serialization-attributes:
public class Bird {
[JsonProperty("type")]
public string _type {get; set;}
[JsonProperty("nameOfBird")]
public string _nameOfBird {get; set;}
}
or even
[JsonProperty("type")]
public string AnyCompletelyDifferentName { get; set; }
Apart from this thereĀ“s no convention in C# for prefixes on public members. Maybe there exists one within your company, however.
I don't know about the _ prefix for attributes is a C# naming convention, but, assuming that you are right, at least for your company, there is a very nice way to solve the paradox you've just presented. Create:
private string _nameOfBird;
public string nameOfBird {
get {
return _nameOfBird;
}
set {
_nameOfBird = value;
}
}
and this way you respect both conventions.

Categories