Load multiple bindings with Ninject - c#

I am trying to load bindings in a class for a project. I am using 3rd party extensions for Caching and the class I need to load looks like below using c# and .net framework 472 .
public class CouchbaseCache : ICouchbaseCache, IDistributedCache
{
public CouchbaseCache(ICouchbaseCacheBucketProvider provider, IOptions<CouchbaseCacheOptions> options);
public IBucket Bucket { get; }
public CouchbaseCacheOptions Options { get; }
}
usually, If I have to load, I would use something like
Bind().To().InSingletonScope();
But how would I do it for the above class by giving the bucket info and Options as values while loading it? I could not get my head around it.
Also, ICouchbaseCachebucketProvider is an interface derived from INamedbucketProvider and derived class looks like
public interface INamedBucketProvider
{
string BucketName { get; }
IBucket GetBucket();
}
So far, I was able to get CouchbaseClientDefinition set up like this
Bind<ICouchbaseClientDefinition>().ToMethod(ctx =>
{
var options = new CouchbaseClientDefinition
{
Servers = new List<Uri>
{
new Uri("http://couchbase.com/")
}
};
return options;
}).InSingletonScope();
I need to give Uri for couchbase and also bucket name and the logic is all over the place. Any knowledge sharing will be greatly appreciated.

if the argument for the constructor of the CouchbaseCache is identical for the whole application life time then you can bind it with the use of binding with constructor arguments something like this where you are loading:
var options = new CouchbaseClientDefinition
{
Servers = new List<Uri>
{
new Uri("http://couchbase.com/")
}
};
var couchbaseCacheBucketProvider= new CouchbaseCacheBucketProvider
{
...
};
Bind<ICouchbaseClientDefinition().To<CouchbaseCache >()
.WithConstructorArgument(couchbaseCacheBucketProvider, options);
but you have to provide the couchbaseCacheBucketProvider.
if the arguments are different but they are limited for example if you have two version of the arguments you can use the named binding like this
var options1 = new CouchbaseClientDefinition
{
...
};
var options2 = new CouchbaseClientDefinition
{
...
};
var couchbaseCacheBucketProvider1= new CouchbaseCacheBucketProvider
{
...
};
var couchbaseCacheBucketProvider2= new CouchbaseCacheBucketProvider
{
...
};
Bind < ICouchbaseClientDefinition().To<CouchbaseCache>().WithConstructorArgument(couchbaseCacheBucketProvider, options1).Named("FirstBinding");
Bind < ICouchbaseClientDefinition().To<CouchbaseCache>().WithConstructorArgument(couchbaseCacheBucketProvider, options2).Named("SecondBinding");
However another Alternative is to use the FactoryPattern/Singleton to create your CouchbaseCache object. Then you need just to inject the Factory Class that you created and you can use the Factory Class to get the required CouchbaseCache object whenever it is required.

Related

Accessing fields filter parameters in IResourceService in JsonApiDotNetCore

I am currently trying to set up a Non-Entity Framework environment to access data via REST/JSON:API in ASP.NET Core 3.1 with https://github.com/json-api-dotnet/JsonApiDotNetCore
I followed the example as shown in: https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/Examples/NoEntityFrameworkExample/Services/WorkItemService.cs
So here is my sample method:
public class DepartmentResourceService : IResourceService<Department>
{
public Task<IReadOnlyCollection<Department>> GetAsync(CancellationToken cancellationToken)
{
IReadOnlyCollection<Department> departments = new List<Department>{
new Department{ Id = 1, Name = "SE", Contact = "se#someaddress.at" },
new Department{ Id = 2, Name = "SD", Contact = "sd#someaddress.at" }
}.AsReadOnly();
return Task.FromResult(departments);
}
...
}
My example works pretty well, but I haven't figured out how I can access the given JSON:API fields-filter.
However: The filters do apply automatically somehow and only the given fields are sent which were defined in the query string, but the filter is applied after the object was generated.
When using EF, I can see that the sql-queries are already limited to the defined JSON:API fields-filter list, therefore the object is only filled up with informationen that was requested and nothing else.
I would like to do the same without EF, but I am missing that filter information in order to do so.
I could figure out, that there is an Interface called ITargetedFields (https://github.com/json-api-dotnet/JsonApiDotNetCore/blob/master/src/JsonApiDotNetCore/Resources/TargetedFields.cs/) and I thought maybe this could be injected into the constructor like so:
public class DepartmentResourceService : IResourceService<Department>
{
private readonly ITargetedFields targetedFields;
public DepartmentResourceService(ITargetedFields targetedFields)
{
this.targetedFields = targetedFields;
}
...
}
But the properties Attributes and Relationships Collections of ITargetedFields are always zero-length.
I couldn't find something in the docs or examples.
Any ideas?
I finally found a way, how to access query information, such as fields, pagination, includes in IResourceService<TResource>:
There are several IQueryConstraintProvider service providers registered in JsonApiApplicationBuilder in the method AddQueryStringLayer() with a Scope-Lifetime:
IIncludeQueryStringParameterReader
IFilterQueryStringParameterReader
ISortQueryStringParameterReader
ISparseFieldSetQueryStringParameterReader
IPaginationQueryStringParameterReader
IResourceDefinitionQueryableParameterReader
They are injected by DI in the constructor of your IResourceService<TResource> implementation, here is an example:
public class DepartmentResourceService : IResourceService<Department>
{
private readonly ISparseFieldSetQueryStringParameterReader _sparseFieldSetQueryStringParameterReader;
private readonly IIncludeQueryStringParameterReader _includeQueryStringParameterReader;
private readonly ISortQueryStringParameterReader _sortQueryStringParameterReader;
private readonly IPaginationQueryStringParameterReader _paginationQueryStringParameterReader;
public DepartmentResourceService(
ISparseFieldSetQueryStringParameterReader sparseFieldSetQueryStringParameterReader,
IIncludeQueryStringParameterReader includeQueryStringParameterReader,
ISortQueryStringParameterReader sortQueryStringParameterReader,
IPaginationQueryStringParameterReader paginationQueryStringParameterReader
)
{
_sparseFieldSetQueryStringParameterReader = sparseFieldSetQueryStringParameterReader;
_includeQueryStringParameterReader = includeQueryStringParameterReader;
_sortQueryStringParameterReader = sortQueryStringParameterReader;
_paginationQueryStringParameterReader = paginationQueryStringParameterReader;
}
public Task<IReadOnlyCollection<Department>> GetAsync(CancellationToken cancellationToken)
{
// Accessing all provided information:
IReadOnlyCollection<ExpressionInScope> constraints = _sparseFieldSetQueryStringParameterReader.GetConstraints();
IReadOnlyCollection<ExpressionInScope> includes = _includeQueryStringParameterReader.GetConstraints();
IReadOnlyCollection<ExpressionInScope> sortQuery = _sortQueryStringParameterReader.GetConstraints();
IReadOnlyCollection<ExpressionInScope> pagination = _paginationQueryStringParameterReader.GetConstraints();
// ***************************************************************
// Do what ever you need to do, with the information provided here
// ***************************************************************
// Return something
IReadOnlyCollection<Department> departments = new List<Department>{
new Department{ Id = 1, Name = "SE", Contact = "se#someaddress.at" },
new Department{ Id = 2, Name = "SD", Contact = "sd#someaddress.at" }
}.AsReadOnly();
return Task.FromResult(departments);
}
...
}

MobileServiceClient with different Model name than table name

Is there a way in Xamarin using MobileServiceClient to add an attribute to the class name to have a different table name?
i.e
this._client = new MobileServiceClient("myurl");
this._blogTable = _client.GetTable<Blog>();
But the table on the server is XCHX_Blogs
I would like to have my model class something like this
[TableName("XCHX_Blogs")]
public class Blog {
...
}
I cant seem to find a way to do this mapping in Xamarin forms (in the model).
In order to do exactly what you are asking you'd have to pull the Mobile Service Client SDK source into your app (instead of using the Nuget) so that you could use the internal MobileServiceTable<T> constructor directly:
this._blogTable = new MobileServiceTable<Blog>("XCHX_Blogs", this._client);
Alternatively you can use the non-generic MobileServiceTable, but then you'll have to handle the JSON de/serialization:
this._blogTable = _client.GetTable("XCHX_Blogs");
var blog = new Blog();
await this._blogTable.InsertAsync(JObject.FromObject(blog));
You can use DataContractAttribute or JsonContainerAttribute or DataTableAttribute
[DataContractAttribute(Name = "tableName")]
public class MyClass { ... }
IMobileServiceTable<MyClass> table = client.GetTable<MyClass>();
or
[JsonObjectAttribute(Title = "tableName")]
public class MyClass { ... }
IMobileServiceTable<MyClass> table = client.GetTable<MyClass>();
or
[DataTableAttribute("tableName")]
public class MyClass { ... }
IMobileServiceTable<MyClass> table = client.GetTable<MyClass>();
Personaly, I prefer the last solution because naming is coherent with type of objects.

How to set custom templateBase as base template

I made custom template using TemplateBase. How to set my template as razor base template. I managed to do this using old api but than I had problem with caching. In new api caching seems much easier but I can't find any example of setting own template as base template.
In your start up routine or similar, add the following
var templateConfig = new TemplateServiceConfiguration
{
BaseTemplateType = typeof(YourCustomTemplateBase<>)
};
var service = RazorEngineService.Create(templateConfig);
Engine.Razor = service;
Add your template base should be created as
public abstract class YourCustomTemplateBase<T> : TemplateBase<T>
{
public string CustomString { get; set; }
}

dealing with dynamic properties on oData client

I have the following class on both server and client
public class Entity
{
public string Id {get; set;}
public string Name {get; set;}
public Dictionary<string, object> DynamicProperties {get; set;}
}
As far as I have seen all the examples of open type describes about having dynamic properties on the server side, but the properties on the client needs to be explicitly declared.When I send a POST request from the client how do i send the dynamic properties ?. I can't declare all the dynamic properties on the client side. There are numerous properties and each object will contain different set of dynamic properties in the client side. These dynamic properties are stored in the DynamicProperties dictionary in the client side. How do I send the object of above entity class to the server side, so that server will interpret the contents of DynamicProperties dictionary as dynamic properties ?. Any help is appreciated.
===========================Follow-up for sam's answer=======================
static void Main(string[] args1)
{
container.Customers.ToList();
Customer newCustomer = new Customer();
newCustomer.Id = 19;
newCustomer.Properties = new Dictionary<string, object>
{
{"IntProp", 9},
{"DateTimeOffsetProp", new DateTimeOffset(2015, 7, 16, 1, 2, 3, 4, TimeSpan.Zero)},
{"blah","ha"}
};
try
{
addCustomer(newCustomer);
container.AddToCustomers(newCustomer);
container.SaveChanges();
}
catch (Exception)
{
}
Customer newCustomer1 = new Customer();
newCustomer1.Id = 20;
newCustomer1.Properties = new Dictionary<string, object>
{
{"IntProp", 10},
{"dir","north"}
};
addCustomer(newCustomer1);
container.AddToCustomers(newCustomer1);
container.SaveChanges();
newCustomer1.Properties["dir"] = "south";
container.UpdateObject(newCustomer1);
container.SaveChanges();
Console.ReadKey();
}
private static void addCustomer(Customer customer)
{
container.Configurations.RequestPipeline.OnEntryStarting(args =>
{
foreach (var property in customer.Properties)
{
args.Entry.AddProperties(new ODataProperty
{
Name = property.Key,
Value = property.Value // for enum, complex type, should to create ODataEnumValue and ODataComplexValue.
});
}
});
}
I am getting an error stating Multiple properties with the name 'IntProp' were detected in an entry or a complex value. In OData, duplicate property names are not allowed. Also, I doubt if creating an action each time before sending an object like how I am doing now is a valid approach as I get lot of objects from a source and I send it to the server. If I create an action for each object then it might blow up the memory as oData client holds these actions in memory. How do I handle my scenario ?. Kindly help me.
Also, one more question if I comment the container.Customers.ToList() it fails stating that I am trying to add undeclared properties. Why is that ?
If you are using OData Client Code Generator, you can use the partial class to define/retrieve/save the dyanmic properties.
For example, in your client side, you can define a partial class for your Entity
public partial class Entity
{
// Dynamic property "Email"
[global::Microsoft.OData.Client.OriginalNameAttribute("Email")]
public string Email
{
get
{
return this._Email;
}
set
{
this.OnEmailChanging(value);
this._Email = value;
this.OnEmailChanged();
this.OnPropertyChanged("Email");
}
}
private string _Email;
partial void OnEmailChanging(string value);
partial void OnEmailChanged();
}
Then, you can use this to insert/retrieve/save the dynamic property "Email".
You can do like this:
Container container = new Container(new Uri("http://..."));
Entity entity = new Entity();
...
entity.Email = "xxxx";
container.AddToEntities(entity);
container.SaveChanges();
For similar implementation, you can refer to my sample project.
========== iteration 2 ================
For client Entity class with IDictionary<string,object>, I think the hook is what you're looking for.
For example, on client side:
public partial class Entity
{
public IDictionary<string, object> Properties { get; set; }
.....
}
It should work if you insert the following codes before
container.AddToEntities(entity);
For example:
Entity entity = new Entity();
...
entity.Properties = new Dictionary<string, object>
{
{"IntProp", 9},
{"DateTimeOffsetProp", new DateTimeOffset(2015, 7, 16, 1, 2, 3, 4, TimeSpan.Zero)}
};
container.Configurations.RequestPipeline.OnEntryStarting(args =>
{
foreach (var property in entity.Properties)
{
args.Entry.AddProperties(new ODataProperty
{
Name = property.Key,
Value = property.Value
});
}
});
container.AddToEntities(entity);
container.SaveChanges();
Where, AddProperties is an extension method. You can find it in my sample project
and the latest commit
Besides, the hood method only works with OData Client V6.12 or above.
Hope it can help you.
========== iteration 3 ================
First, you call the following method,
container.Configurations.RequestPipeline.OnEntryStarting(...);
It means to add an action which will be called in later execution. In your codes, you call it twice, So, there are two actions added. These two actions will be called one by one when execute to save your newCustomer1
That's, newCustomer1 will have newCustomer's dynamic properties (action 1), meanwhile, it will have its own dynamic properties (action 2). That's why you got the duplicate property name exception.
To resolve it, you can just to renew a Container. See my project's update.
For container.Customers.ToList(), it seems an OData client issue.
[Answering my own question : Another approach]
Extending Sam Xu's approach for iteration 2. We can do it as below. (For the sake of clarity let's assume the name of the class in question as Book)
public partial class Book
{
public string ISBN {get; set;}
public IDictionary<string, object> DynamicProperties { get; set; }
}
// This portion can be put in a function and can be invoked only once
container.Configurations.RequestPipeline.OnEntryStarting(args =>
{
if(args.Entity.GetType() == typeof(Book))
{
var book = args.Entity as Book
foreach (var property in book.DynamicProperties)
{
args.Entry.AddProperties(new ODataProperty
{
Name = property.Key,
Value = property.Value
});
}
}
});
AddProperties extension method implementation is provided in Sam Xu's implementation

How to force mongo to store members in lowercase?

I have a collection of BsonDocuments, for example:
MongoCollection<BsonDocument> products;
When I do inserts into the collection, I want the member name to always be lowercase. After reading the documentation, it appears that ConventionPack is the way to go. So, I've defined one like this:
public class LowerCaseElementNameConvention : IMemberMapConvention
{
public void Apply(BsonMemberMap memberMap)
{
memberMap.SetElementName(memberMap.MemberName.ToLower());
}
public string Name
{
get { throw new NotImplementedException(); }
}
}
And right after I get my collection instance I register the convention like this:
var pack = new ConventionPack();
pack.Add(new LowerCaseElementNameConvention());
ConventionRegistry.Register(
"Product Catalog Conventions",
pack,
t => true);
Unfortunately, this has zero effect on what is stored in my collection. I debugged it and found that the Apply method is never called.
What do I need to do differently to get my convention to work?
In order to use IMemeberMapConvention, you must make sure to declare your conventions before the mapping process takes place. Or optionally drop existing mappings and create new ones.
For example, the following is the correct order to apply a convention:
// first: create the conventions
var myConventions = new ConventionPack();
myConventions.Add(new FooConvention());
ConventionRegistry.Register(
"My Custom Conventions",
myConventions,
t => true);
// only then apply the mapping
BsonClassMap.RegisterClassMap<Foo>(cm =>
{
cm.AutoMap();
});
// finally save
collection.RemoveAll();
collection.InsertBatch(new Foo[]
{
new Foo() {Text = "Hello world!"},
new Foo() {Text = "Hello world!"},
new Foo() {Text = "Hello world!"},
});
Here's how this sample convention was defined:
public class FooConvention : IMemberMapConvention
private string _name = "FooConvention";
#region Implementation of IConvention
public string Name
{
get { return _name; }
private set { _name = value; }
}
public void Apply(BsonMemberMap memberMap)
{
if (memberMap.MemberName == "Text")
{
memberMap.SetElementName("NotText");
}
}
#endregion
}
These are the results that came out when I ran this sample. You could see the Text property ended up being saved as "NotText":
If I understand correctly, conventions are only applied when auto-mapping. If you have a classmap, you need to explicitly call AutoMap() to use conventions. Then you can modify the automapping, e.g.:
public class MyThingyMap : BsonClassMap<MyThingy>
{
public MyThingyMap()
{
// Use conventions to auto-map
AutoMap();
// Customize automapping for specific cases
GetMemberMap(x => x.SomeProperty).SetElementName("sp");
UnmapMember(x => x.SomePropertyToIgnore);
}
}
If you don't include a class map, I think the default is to just use automapping, in which case your convention should apply. Make sure you're registering the convention before calling GetCollection<T>.
You can define ConventionPack which is also part of their official document on Serialization. Like below which stores are property names as camel case. You can place while Configuring services/repositories
Official link
https://mongodb.github.io/mongo-csharp-driver/1.11/serialization/[Mongo Db Serialization C#]1
// For MongoDb Conventions
var pack = new ConventionPack
{
new CamelCaseElementNameConvention()
};
ConventionRegistry.Register(nameof(CamelCaseElementNameConvention), pack, _ => true);

Categories