As the title suggests, I am wondering if there is an efficient way to leverage the SCAN/Pattern. I am using c# .net core. When I search into the IDatabase, I can't really find anything other than SetScan that would allow for a pattern, but this is clearly not the correct thing. This is a bit unusual to me as the redis console in Azure Redis does support the concept of SCAN.
I need to be able to pass a partial key here:
Assume I can store keys like this: "SID_{0}_CID_{1}_OID_{2}_BID_{3}"
///BUT when I call Redis, I might only have part of the key
RedisValue value = await _redisConnection.BasicRetryAsync(async (db) => await db.StringGetAsync(key)); //<-- key needs to be like '*CID_123*BID_333*'
if(value.HasValue)
return value;
return string.Empty;
db implements IDatabase which implements IRedis and IDatabaseAsync
Anyone have an idea how I might achieve this? Python guy seems to have this available but I can't find similar.. Redis wildcard keys on get
I ended up doing the following:
public async Task<RedisValue[]> GetItems(string customPattern)
{
var endpoints = _connection.GetEndPoints();
var s= _connection.GetServer(endpoints[0]);
var keys = s.Keys(pattern: customPattern).ToArray();
return await _database.StringGetAsync(keys);
}
to riff the Northern Pikes... 'it ain't pretty it just looks that way.'
Related
I want to build my own mysql class. But I'm relative new to c#.
So in my thoughts I would like to build some like:
mySqlTool.select("a,b,c").from("foo").where("x=y");
I dont know how this is called and actually I dont really know if this is even possible. My google search ended with no real result.
So my questions are:
Is it possible to do some like my sample above?
Creating a fluent api isn't overly complicated. You simply return the current instance of the class from each method which allows them to be chained the way that you specify in your post:
public class MySqlTool
{
private string _fields, _table, _filters;
public MySqlTool Select(string fields)
{
_fields = fields;
return this;
}
public MySqlTool From(string table)
{
_table = table;
return this;
}
public MySqlTool Where(string filters)
{
_filters = filters;
return this;
}
public Results Execute()
{
// build your query from _fields, _table, _filters
return results;
}
}
Would allow for you to run a query like:
var tool = new MySqlTool();
var results = tool.Select("a, b, c").From("someTable").Where("a > 1").Execute();
It looks like you are trying to do something similar to Linq to SQL, but with MySQL instead. There is a project called LinqConnect that does this. I have no affiliation with them.
You can use it like this (from a LinqConnect tutorial):
CrmDemoDataContext context = new CrmDemoDataContext();
var query = from it in context.Companies
orderby it.CompanyID
select it;
foreach (Company comp in query)
Console.WriteLine("{0} | {1} | {2}", comp.CompanyID, comp.CompanyName, comp.Country);
Console.ReadLine();
I'm not a big fan of it personally, but it can be useful if you're just learning. Linq2SQL can give you that functionality somewhat out of the box.
If you're looking to accomplish this yourself. You'll want to turn to extension methods. A feature within c# that will allow you to extend your classes using static methods, operating on the base.
My recommendation if you're doing your own data-access layer, then you should use something like dapper (built/used by the crew at StackOverflow). Which offers you a simple wrapper around the base connection to the database. It offers simple ORM functionality and does parameterization for you.
I would also recommend strongly that you encapsulate your intensive queries within Stored Procedures.
What you're looking to do is design a fluent interface, and it's somewhat of an advanced concept for someone who is just learning C#. I would suggest you stick to the basics until you have a little more experience.
More importantly, there are already existing data adapters built into the .NET FCL and also third-party adapters like this one that are more suitable and already do what you're trying to do using "LINQ to (database)". I wouldn't reinvent the wheel if I were you.
I am designing a fluent API for writing SQL. Keep in mind one of my goals is to have API not suggest functions that can't be called in that part of the chain. For instance if you just got done defining a field in the select clause you can't call Where until you called From first. A simple query looks like this:
string sql = SelectBuilder.Create()
.Select()
.Fld("field1")
.From("table1")
.Where()
.Whr("field1 > field2")
.Whr("CURRENT_TIMESTAMP > field3")
.Build()
.SQL;
My problem comes with recursion in SQL code. Say you wanted to have a field contain another SQL statement like below:
string sql = SelectBuilder.Create()
.Select()
.Fld("field1")
.SQLFld()
.Select
.Count("field6")
.From("other table")
.EndSQLFld()
.FLd("field2")
.From("table1")
.Where()
.Whr("field1 > field2")
.Whr("CURRENT_TIMESTAMP > field3")
.Build()
.SQL;
I am using method chaining to build my fluent API. It many ways it is a state machine strewn out across many classes which represent each state. To add this functionality I would need to copy essentially every state I already have and wrap them around the two SQLFld and EndSQLFld states. I would need yet another copy if you were one more level down and were embedding a SQL statement in to a field of the already embedded SQL statement. This goes on to infinity, so with an infinitely deep embedded SQL query I would need an infinite number of classes to represent the infinite states.
I thought about writing a SelectBuilder query that was taken to the point of the Build method and then embedding that SelectBuilder in to another SelectBuilder and that fixes my infinity problem, but it is not very elegant and that is the point of this API.
I could also throw out the idea that the API only offers functions when they are appropriate but I would really hate to do that. I feel like that helps you best discover how to use the API. In many fluent APIs it doesn't matter which order you call what, but I want the API to appear as close to the actual SQL statement as possible and enforce its syntax.
Anyone have any idea how to solve this issue?
Glad to see you are trying fluent interfaces, I think they are a very elegant and expressive.
The builder pattern is not the only implementation for fluent interfaces. Consider this design, and let us know what you think =)
This is an example and I leave to you the details of your final implementation.
Interface design example:
public class QueryDefinition
{
// The members doesn't need to be strings, can be whatever you use to handle the construction of the query.
private string select;
private string from;
private string where;
public QueryDefinition AddField(string select)
{
this.select = select;
return this;
}
public QueryDefinition From(string from)
{
this.from = from;
return this;
}
public QueryDefinition Where(string where)
{
this.where = where;
return this;
}
public QueryDefinition AddFieldWithSubQuery(Action<QueryDefinition> definitionAction)
{
var subQueryDefinition = new QueryDefinition();
definitionAction(subQueryDefinition);
// Add here any action needed to consider the sub query, which should be defined in the object subQueryDefinition.
return this;
}
Example usage:
static void Main(string[] args)
{
// 1 query deep
var def = new QueryDefinition();
def
.AddField("Field1")
.AddField("Filed2")
.AddFieldWithSubQuery(subquery =>
{
subquery
.AddField("InnerField1")
.AddField("InnerFiled2")
.From("InnerTable")
.Where("<InnerCondition>");
})
.From("Table")
.Where("<Condition>");
// 2 queries deep
var def2 = new QueryDefinition();
def2
.AddField("Field1")
.AddField("Filed2")
.AddFieldWithSubQuery(subquery =>
{
subquery
.AddField("InnerField1")
.AddField("InnerField2")
.AddFieldWithSubQuery(subsubquery =>
{
subsubquery
.AddField("InnerInnerField1")
.AddField("InnerInnerField2")
.From("InnerInnerTable")
.Where("<InnerInnerCondition>");
})
.From("InnerInnerTable")
.Where("<InnerCondition>");
})
.From("Table")
.Where("<Condition>");
}
You can't "have only applicable methods available" without either sub-APIs for the substructures or clear bracketing/ending of all inner structural levels (SELECT columns, expressions in WHERE clause, subqueries).
Even then, running it all through a single API will require it to be stateful & "modal" with "bracketing" methods, to track whereabouts in the decl you are. Error reporting & getting these right will be tedious.
Ending bracketing by "fluent" methods, to me, seems non-fluent & ugly. This would result in a ugly appearence of EndSelect, EndWhere, EndSubquery etc. I'd prefer to build substructures (eg SUBQUERY for select) into a local variable & add that.
I don't like the EndSQLFld() idiom, which terminates the Subquery implicitly by terminating the Field. I'd prefer & guess it would be better design to terminate the subquery itself which is the complex part of the nested structure -- not the field.
To be honest, trying to enforce ordering of a "declarative" API for a "declarative" language (SQL) seems to be a waste of time.
Probably what I'd consider closer to an ideal usage:
SelectBuilder select = SelectBuilder.Create("CUSTOMER")
.Column("ID")
.Column("NAME")
/*.From("CUSTOMER")*/ // look, I'm just going to promote this onto the constructor.
.Where("field1 > field2")
.Where("CURRENT_TIMESTAMP > field3");
SelectBuilder countSubquery = SelectBuilder.Create("ORDER")
.Formula("count(*)");
.Where("ORDER.FK_CUSTOMER = CUSTOMER.ID");
.Where("STATUS = 'A'");
select.Formula( countSubquery, "ORDER_COUNT");
string sql = SelectBuilder.SQL;
Apologies to the Hibernate Criteria API :)
Probably right in front of me, but I am not sure how to set the ReadPreference.Secondary setting in the C# driver? I wanted to distribute the query load to my secondary nodes as opposed to the default. I have set slaveOk() on the nodes themselves.
Any help / example would be appreciated. I just can't find a good example of setting that property.
Thanks,
S
EDIT: So maybe ReadPreference hasn't been implemented in C# driver yet...that looks to be the case. So then I would use slaveok?
Something like one of the below?:
var mongoServer = MongoServer.Create("mongodb://localhost/?
replicaSet=myset;slaveOk=true");
var db = mongoServer.GetDatabase("MyDb");
var coll = db.GetCollection("MyColl");
or
var cs= db.CreateCollectionSettings<BsonDocument>("Users");
cs.SlaveOk = true;
var coll = db.GetCollection(cs);
EDIT2:
Looks like I might need to modify connection string to decorate each Mongo Instance as well?
mongodb://serverA:27017,serverB:27017,serverC:27017/?safe=true;replicaset=myreplicaset;slaveok=true
Yes, ReadPreferences have not been implemented in the C# driver. We at 10gen have been waiting to implement ReadPreferences until all the drivers, including mongos, could all implement at the same time. This support should come approximately at the time of server release 2.2.
I have a device which have low level programming. I am giving version numbers every new devices and upgrades. I also have a program which communicate with these devices (to retrieving information on these devices).
For eg. v1.2 sends this kind of string:
v1.2|Time|Conductivity|Repetation|Time|Heat of First Nozzle|Pressure|EndOfMessage
but new version of device program:
v1.3|Time|Conductivity|Repetation|Time|Humadity|1st Nozzle Heat;2nd Nozzle Heat|Pressure|EndOfMessage
My test application will retrieve information and change the operation of this device. Some operations will have in v1.2 device some not. I thought strategy design pattern seems useful for this situation but I'm not sure. Which design pattern should I use to do this?
Yes, this would be a good use-case for the Stategy pattern, although you will also use the Factory pattern to create a specific parser instance.
Your code should then generally look something like this:
public DeviceInfo Parse(InputData input)
{
var version = versionParser.Parse(input);
var concreteParser = parserFactory.CreateFor(version);
var data = concreteParser.Parse(data);
return data;
}
For a simple project with few parsers, you may hardcode your parser factory:
public class ParserFactory
{
public static IParser<DeviceInfo> CreateFor(Version version)
{
// instantiate proper parser based on version
}
}
Depending on the size of your project, you may also decide to use a plugin pattern for your parsers (System.AddIn contains useful classes for managing plugins).
I feel Strategy along with Factory method will solve the purpose.
I'm apologizing if I'm using the wrong terminology here. I'm still very much in the ORM world, but I've been playing around with MongoDb and really love what I see. One of the things I'm not liking is this:
var books = bookRepository.GetCollection<BsonDocument>("books");
And
foreach (var book in books.FindAllAs<Book>())
{
Console.WriteLine("Author: {0}, Title: {1}", book.Author, book.Title);
}
I've found several tutorials on wrapping NoRM in a session but I can't figure out how to do it using the CSharp Drivers (the ones that Mongodb recommends / has on their github page).
What I'd really like to do is something like this for the first example:
var bookRepository = MongoRepository<Book>(); // probably should use IoC to resolve this
and
foreach (var book in books.FindAll())
Voila! I'm probably not the first person to want this, using strings everywhere seems a bit nutty, though I will grant that the tutorial is just an example. Is there a "best practices" example to setting this all up in such a manner?
Edit: Please let me know if this is crazy talk and not how to do things in Mongo, again this is my first test project.
Here is snippet from my project:
public static MongoCollection<T> GetCollection<T>(string collectionName = null)
{
if (string.IsNullOrWhiteSpace(collectionName))
{
Type g = typeof (T);
collectionName = g.Name;
}
return MongoServer.Create(Config.MongoConnectionString).GetDatabase(Config.Database).GetCollection<T>(collectionName);
}
Now I dont need to specify a collection name as a string unless I want to override it:
var collection = GetCollection<MyEntity>();
or
var collection = GetCollection<MyEntity>("SomeOtherCOllection");
You can use some inflection utility\library to pluralize your collection names if you want.
Also, you dont need to specify the type in your Find methods if you specified the type when instantiating a collection class, like I have above.
For example, this is how I do it:
MongoCursor<MyEntity> results = collection.FindAll();
or
MongoCursor<MyEntity> results = collection.Find(query);