Alternatives to passing around a context object - c#

I have a single context object that I want to be able to access from a large number of difference classes. I have code that looks like
Context ctx = new Context();
Section section = new Section(ctx) {
Data1 = new SomeData(ctx) { Value = 123 },
Data2 = new SomeOtherData(ctx) { Foo = "bar" },
SubSection = new Section(ctx) {
MoreData = new MoreData(ctx) { Text = "Hello!" }
}
};
But what I'd really like is code that looks like:
using(Context.New()) {
Section section = new Section() {
Data1 = new SomeData { Value = 123 },
Data2 = new SomeOtherData { Foo = "bar" },
SubSection = new Section {
MoreData = new MoreData { Text = "Hello!" }
}
};
// do something with section
}
Is this possible? I'll be using it in ASP.NET as well as .exes (and probably something else in the future) so I can't just store a static or thread local reference somewhere.
It doesn't need to be exactly as above, just a way where I won't have to pass the context to every object I create. I thought about using extension methods like context.createSomeData() but it requires more boilerplate per class and isn't really any better as you still need the context object.
Ideally should work under VS2008/.NET3.5, although I'd still be interested if there's any way to do it at all.
UPDATE: I ended up solving this by refactoring my approach into the following:
Section section = new Section {
Data1 = new SomeData { Value = 123 },
Data2 = new SomeOtherData { Foo = "bar" },
SubSection = new Section {
MoreData = new MoreData { Text = "Hello!" }
}
};
section.DoStuffWithContext(new Context());
While it may not work for everyone, it does what I need here.
I'll leave this question open in case someone comes up with a good solution to the initial problem.

You can define a static method Context.RetreiveData(), but you don't have to implement any boilerplate code inside of the method itself.
Using command pattern every specific project type can provide its own implementation for the RetreiveData() method. ASP.NET project can supply a method that would retreive data from Session. WinForm executable can supply a method that would retreive a data from some global variable. Yet another project can supply a method for retrieving data from DB.

No. There isn't any clear possibility. You are even using object initializers (new Obj { ... }), so you need to use the new operator (this makes using static methods/extension methods of ctx impossible).
The only thing you could do:
SomeData MakeSomeData(this Context ctx, int value)
{
return new SomeData(ctx) { Value = value };
}
and in the initialization:
Context ctx = new Context();
Section section = new Section(ctx) {
Data1 = ctx.MakeSomeData(123), ...
but I don't think you would gain anything

I second hvd's comment on your question, and I don't think it would require too much boilerplate. Suppose each class implements this:
public interface IContextConsumer {
Context Context { get; set; }
}
And your base class has a method like this:
protected void AddChild(IContextConsumer child) {
child.Context = this.Context;
}
The property implementations would only need to be:
private SomeData _data1;
public SomeData Data1 {
get { return _data1; }
set {
_data1 = value;
AddChild(_data1);
}
}
You could even allow the context to be reassigned on the root, if you did something like this:
protected void AddChild(IContextConsumer child) {
Children.Add(child);
child.Context = this.Context;
}
protected void OnContextChanged() {
foreach (var child in Children) child.Context = this.Context;
}
Derived classes would just need to call OnContextChanged in their Context property implementations.

Related

C# Initialize class properties from REST client inside constructor

I've searched a lot and I think this is possible, but I feel like I am just blocked from knowing how to properly format it.
I have a class representing a product that is a relation class from our CRM to Magento.
Inside the constructor, I have to do some stuff like this...
public Product(IBaseProduct netforumProduct, MagentoClient client)
{
Product existingMagentoProduct = client.GetProductBySku(netforumProduct.Code);
if (existingMagentoProduct != null)
{
this.id = existingMagentoProduct.id;
this.name = existingMagentoProduct.name;
... many of them ...
this.visibility = existingMagentoProduct.visibility;
this.extension_attributes.configurable_product_links = existingMagentoProduct.extension_attributes.configurable_product_links;
}
else
{
// its a new product, new up the objects
this.id = -1;
this.product_links = new List<ProductLink>();
this.options = new List<Option>();
this.custom_attributes = new List<CustomAttribute>();
this.media_gallery_entries = new List<MediaGalleryEntry>();
this.extension_attributes = new ExtensionAttributes();
this.status = 0; // Keep all new products disabled so they can be added to the site and released on a specific day (this is a feature, not an issue / problem).
this.attribute_set_id = netforumProduct.AttributeSetId;
this.visibility = 0;
}
}
It seems silly to have to initialize all of the properties like that. I could use a mapper but that seems like a bandaid. I have to see if the product exists in magento first, and populate its ID and values, otherwise whenever I save the product it creates an additional one.
I considered doing the class constructor calling a static method, but I couldn't get the syntax right.
It might just be too late and I need to think about something else for awhile.
If you must do it in the constructor, you can get rid of a lot of code by first setting 'default' values to the 'Product' properties. This will remove the need to do them in the constructor. Next, if you wanted to automatically set the class's properties, you can use reflection.
public class Product
{
public int Id { get; set; } = -1;
public List<ProductLink> Product_Links { get; set; } = new List<ProductLink>();
....
public int Visibility { get; set; } = 0;
public Product(IBaseProduct netforumProduct, MagentoClient client)
{
var existingMagentoProduct = client.GetProductBySku(netforumProduct.Code);
if (existingMagentoProduct != null)
{
foreach (PropertyInfo property in typeof(Product).GetProperties().Where(p => p.CanWrite))
{
property.SetValue(this, property.GetValue(existingMagentoProduct, null), null);
}
}
}
}
Though, I would like to point out that you probably shouldn't be using a REST client inside a class constructor, especially to just populate its data (also, you are performing a synchronous operation). It would be cleaner to have another layer that is responsible for populating this class using the client, and then use something like AutoMapper to map the data to it.

How to add data one level down to IEnumerable type?

I am working on .Net project. I have my Product model below.
class Product
{
public IEnumerable<OptionData> Options { get; set; }
}
Then I have OptionData model below.
public class OptionData
{
public Colour PrimaryColour { get; set; }
public Colour SecondaryColour { get; set; }
public IEnumerable<SizeData> Sizes { get; set; }
}
Then I have SizeData model below.
public class SizeData
{
public string KeycodeNumber { get; set; }
public Size Size { get; set; }
}
Then I have my size model below.
public class Size
{
public string Name { get; set; }
}
Then I am sending data using these models to some messaging system. In my case it is confluent kafka.
Options = new Option[]
{
new Option
{
PrimaryColour = new CodeNamePair
{
Name = "White",
},
SecondaryColour = new CodeNamePair
{
Name = "red",
},
Sizes = new SizeElement[]
{
new SizeElement
{
Size = new KafkaProductEvent.Size
{
Name = "10"
},
KeycodeNumber = 232
}
}
}
}
Then through consumer I am extracting data. I am able to get PrimaryColour or SecondaryColour as below.
IEnumerable<object> options = (IEnumerable<object>)((GenericRecord)response.Message.Value["Product"])["Options"];
foreach (var data in options)
{
OptionData optionData = new OptionData()
{
PrimaryColour = new Colour()
{
Name = (string)((GenericRecord)((GenericRecord)data)["PrimaryColour"])["Name"],
},
SecondaryColour = new Colour()
{
Name = (string)((GenericRecord)((GenericRecord)data)["SecondaryColour"])["Name"]
}
};
}
Then I want to get Sizes data as well. I tried something like this.
Sizes = new SizeData[]
{
new SizeData()
{
Size = new ProductEvents.Size()
{
Name = "";
}
}
}
I am not sure how to get size name from above. Can someone help me to find it out. Any help would appreciated. Thanks
Main challenge that I see in the code posted by you is the kind of APIs exposed by the client adapter you are using for deserializing the complex data model with multiple aggregated objects, challenge with what you are doing is, typecasting every record in every hierarchy to GenericRecord, then typecast to actual .Net type object, which means as the aggregated hierarchy grows, it will make it extremely complex to deserialize the actual object, especially with aggregated collections:
Also a point related to deserializing Options :
class Product
{
public IEnumerable<OptionData> Options { get; set; }
}
Your code is:
IEnumerable<object> options = (IEnumerable<object>)((GenericRecord)response.Message.Value["Product"])["Options"];
What I am wondering is why you cannot type cast to IEnumerable<OptionData>, to make it little simple and let's assume if that's not possible, then while enumerating through the IEnumerable<object> options why it can't still be type casted to the OptionData object, challenge with the adapter or approach you are using is that it needs complete object and hierarchy / property name awareness to de-serialize, when ideally once you fill the top level object like Product in this case, rest all shall recursively fill in, a good example is Newtonsoft Json, it will automatically fill object of any complexity, will make anything unavailable as null / default and require the minimal deserialization code.
Actually what you can do is, develop your own adapter that reads property details via reflection and fill data that is available in the input or discard it. For now assuming, this is all that you have as APIs, then following shall be the approach:
IEnumerable<object> options = (IEnumerable<object>)((GenericRecord)response.Message.Value["Product"])["Options"];
foreach (var data in options)
{
OptionData optionData = new OptionData()
{
PrimaryColour = new Colour()
{
Name = (string)((GenericRecord)((GenericRecord)data)["PrimaryColour"])["Name"],
},
SecondaryColour = new Colour()
{
Name = (string)((GenericRecord)((GenericRecord)data)["SecondaryColour"])["Name"]
},
Sizes = new List<SizeData>() // Initialize Collection Here
};
IEnumerable<object> SizesEnumerable = (IEnumerable<object>)(((GenericRecord)data)["Sizes"]);
foreach (var size in SizesEnumerable)
{
var sizeValue = new SizeData
{
KeycodeNumber = (string)((GenericRecord)size)["KeycodeNumber"],
Size = new Size
{
Name = (string)((GenericRecord)((GenericRecord)size)["Size"])["Name"]
}
};
((List<SizeData>)optionData.Sizes).Add(sizeValue); // Add Data here
}
}
What's the difference ?
You are trying to use the object initializer to fill in the IEnumerable<SizeData> Sizes to fill in the collection, but that doesn't provide option to do further processing as required in your case
Also note I have made IEnumerable<SizeData> Sizes as List<SizeData>, since we cannot use object initializer, so we cannot use array, since we don't know the size in advance
Going further used the same logic as yours to fill the data

IEnumerable<T> GroupBy with Fluent Validation

So the problem I am trying to solve is that I am using a lot of generic classes with <T> that need to execute a .NET Async REST call to retrieve an IEnumerable<T> list of objects from an API. At runtime things are resolved fine with the T stuff because I have some concrete instances higher up the chain.
I have a worker class:
public class Worker<T> where T : class, new()
That has a REST client factory:
IBatchClientFactory batchClientFactory
where in that factory basically creates an instance of this:
public class BatchClient<T> where T : class, new()
That BatchClient has an important method:
public BaseBatchResponse<T> RetrieveManyAsync(int top = 100, int skip = 0)
so that the worker class's method does something like:
var batchClient = this.batchClientFactory.Create<T>(siteId);
var batchResponse = await batchClient.RetrieveManyAsync(top, skip);
Batch Response looks like:
public class BaseBatchResponse<T>
{
public List<T> Value { get; set; }
public BaseBatchResponse<T> Combine(BaseBatchResponse<T> baseBatchResponse)
{
return new BaseBatchResponse<T>
{
Value = this.Value.Concat(baseBatchResponse.Value).ToList()
};
}
}
Now at runtime things are ok because higher up the chain i will instantiate Worker into something like.. new Worker<Appointment>(); And the T's will all just work perfectly since everything down the chain is just doing generics.
My problem now is that I would like to evaluate my batchResponse and go through the List and run some validation against each element in the list. I saw this article on stack overflow that seems to let you split a list into 2 lists using GroupBy via a Dictionary where some SomeProp is the thing you're splitting around.. but can you do that GroupBy logic using a method call? And more importantly can I use FluentValidation as that method call? Ideally my code would look like:
var groups = allValues.GroupBy(val => validationService.Validate(val)).ToDictionary(g => g.Key, g => g.ToList());
List<T> valids = groups[true];
List<T> invalids= groups[false];
Where the result would be a List of my objects that are valid, and a second List of my objects that are invalid.
Ideally I would then just make a FluentValidation class that binds to my concreate Appointment class and has a rule inside it:
this.When(x => !string.IsNullOrWhiteSpace(x.Description), () =>
this.RuleFor(x => x.Description).Length(1, 4000));
Which will hook everything together and be used to determine if my object at runtime belongs in the valids or invalids list
I'm not sure fluent means, there is a approch to achieve that using LINQ:
using System.Collections.Generic;
using System.Linq;
namespace Investigate.Samples.Linq
{
class Program
{
public class SomeEntity
{
public string Description { get; set; }
}
static void Main(string[] args)
{
//Mock some entities
List<SomeEntity> someEntities = new List<SomeEntity>()
{
new SomeEntity() { Description = "" },
new SomeEntity() { Description = "1" },
new SomeEntity() { Description = "I am good" },
};
//Linq: Where to filter out invalids, then category to result with ToDictionary
Dictionary<bool, SomeEntity> filteredAndVlidated = someEntities.Where(p => !string.IsNullOrWhiteSpace(p.Description)).ToDictionary(p => (p.Description.Length > 1));
/* Output:
* False: new SomeEntity() { Description = "1" }
* True: new SomeEntity() { Description = "I am good" }
* */
}
}
}
Code segment:
Dictionary<bool, SomeEntity> filteredAndVlidated = someEntities.Where(p => !string.IsNullOrWhiteSpace(p.Description)).ToDictionary(p => (p.Description.Length > 1));

How to parse a simple class to Bson and to a MongoDB collection

I'm brand new to MongoDB in C#.
I have created a very simple class which I would like to automatically insert into a collection.
How do I do that, if I don't want to map everything manually?
public class DummyClass
{
[BsonId]
public int Id { set; get; }
[BsonElement("first")]
public string First { set { _name = value; } }
[BsonConstructor]
public DummyClass()
{
Id = 2;
First = "1";
}
}
I had hoped I could do something like this:
_dbClient = new MongoClient();
_database = _dbClient.GetDatabase("testDB");
_collection = _database.GetCollection<BsonDocument>("Collection");
var doc = BsonDocument.Create(dummy);
_collection.InsertOneAsync(doc);
But it's no good. I get the exception:
System.ArgumentException : .NET type DummyClass cannot be mapped to BsonType.Document.
Parameter name: value
Any suggestions?
And I really don't want to do:
{
{"Id", "2"},
{"First", "1"},
}
EDIT:
I forgot this small line:
BsonClassMap.RegisterClassMap<DummyClass>();
It does wonders.
It makes sense to use BsonDocument when fields in collections don't match properties in your model class. Otherwise you should create collection mapped to your class.
_collection = _database.GetCollection<DummyClass>("Collection");
await _collection.InsertOneAsync(doc);
And don't forget about async/await methods in MongoDB.Driver.
You can use ToBsonDocument()
_collection.InsertOneAsync(doc.ToBsonDocument())

A more concise syntax in C# constructor?

I have a constructor something like the following:
using Microsoft.Data.Extensions;
public class Complaint
{
public int Id {get; set;}
public int Transcript {get; set;}
//... etc. ... Lots more properties
public Complaint(int id)
{
var command = dataContext.CreateStoreCommand(
"dbo.stp_Complaint_Get",
CommandType.StoredProcedure,
new SqlParameter("Id", id));
var complaint = command.Materialize(x =>
new Complaint
{
Id = x.Field<int>("Id"),
Transcript = x.Field<string>("Transcript");
//... etc. ... Lots more fields from db
}
this.Id = complaint.Id;
this.Transcript = complaint.Transcript;
//... etc. ... Lots more properties to set
}
}
Is there a syntax in C# that would allow me to carry out the last part in one step instead of two? i.e. conceptually something like this:
this = command.Materialize(x =>
new Complaint
{
Id = x.Field<int>("Id"),
Transcript = x.Field<string>("Transcript");
}
Well, you could use a static method instead of a constructor:
public static Complaint FromId(int id)
{
var command = dataContext.CreateStoreCommand(
"dbo.stp_Complaint_Get",
CommandType.StoredProcedure,
new SqlParameter("Id", id));
return command.Materialize(x =>
new Complaint
{
Id = x.Field<int>("Id"),
Transcript = x.Field<string>("Transcript");
//... etc. ... Lots more fields from db
});
}
Not inherently. You could store the complaint object inside the class, and have all the properties point off that rather than setting them all from the constructor.
eg
public class Complaint
{
private readonly {type of complaint} m_Complaint;
public int Id
{
get { return m_Complaint.Id; }
}
// ...etc
}
You could get more complicated if you don't have a setter on m_Complaint - keeping nullable backing fields, and check that before you access the m_Complaint properties
I believe you may try something like this:
var currentInstance = this;
var complaint = command.Materialize(x =>
new Complaint
{
Id = currentInstance.Id = x.Field("Id"),
Transcript = currentInstance.Transcript = x.Field("Transcript");
//... etc. ... Lots more fields from db
});
I don't think you can capture this in the closure, so using that currentInstance trick should help, however it may as well turn out to be redundant.
Besides, code such as that one is sort of obfuscated compared to your current solution. I believe that your code is pretty fine as it is.
I gather that you're trying to avoid setting all of those properties twice, because any change to the structure requires you to update the properties in two places, is that correct?. You could try to use some reflection to copy the properties from one instance to another.
var complaint = command.Materialize(x =>
new Complaint
{
Id = x.Field<int>("Id"),
Transcript = x.Field<string>("Transcript");
//... etc. ... Lots more fields from db
}
foreach (var prop in typeof(Complaint).GetProperties())
...
Have the complaint object as a member variable and the get/set properties accesses the underlying complaint's properties?

Categories

Resources