FluentMongo throwing error all of a sudden - mongodb

I am using FluentMongo and the MongoDBCSharpDriver. My code was working fine for a while, but after updating my MongoCSharpDriver, I now I keep getting this error when I try to query the database:
"Discriminators can only be registered for classes, not for interface MyLib.Services.IRepoData."
The interface IRepoData is just one that I use for all my objects saved to MongoDB. It just defines _id for everything. Here is the line that is breaking:
var item = Collection.AsQueryable().SingleOrDefault(a => a.Id == itemID);
Can anyone shed some light on this one? If I just use .SingleOrDefault() with no lambda then it works fine, its passing a lambda that breaks it.
EDIT
In case this helps...
var Collection = GetCollection<MyClass>();
private MongoCollection<T> GetCollection<T>() where T : class, new()
{
string typeName = typeof(T).Name;
var collection = db.GetCollection<T>(typeName, safeMode);
return collection;
}

Found it! I was calling GetCollection() from within another generic method, like this:
public T Save<T>(T item) where T : class, IRepoData, new()
{
GetCollection<T>().Save(item);
}
This caused GetCollection to see T as the interface instead of the actual instance class. GetCollection works fine anywhere else.
For anyone else with this problem, I just used a low level query like this instead... Collection.FindOneAs<T>(Query.EQ("Id", itemID.ToString()));

Related

Non-static method cannot be called statically [duplicate]

Im trying to load my model in my controller and tried this:
return Post::getAll();
got the error Non-static method Post::getAll() should not be called statically, assuming $this from incompatible context
The function in the model looks like this:
public function getAll()
{
return $posts = $this->all()->take(2)->get();
}
What's the correct way to load the model in a controller and then return it's contents?
You defined your method as non-static and you are trying to invoke it as static. That said...
1.if you want to invoke a static method, you should use the :: and define your method as static.
// Defining a static method in a Foo class.
public static function getAll() { /* code */ }
// Invoking that static method
Foo::getAll();
2.otherwise, if you want to invoke an instance method you should instance your class, use ->.
// Defining a non-static method in a Foo class.
public function getAll() { /* code */ }
// Invoking that non-static method.
$foo = new Foo();
$foo->getAll();
Note: In Laravel, almost all Eloquent methods return an instance of your model, allowing you to chain methods as shown below:
$foos = Foo::all()->take(10)->get();
In that code we are statically calling the all method via Facade. After that, all other methods are being called as instance methods.
Why not try adding Scope? Scope is a very good feature of Eloquent.
class User extends Eloquent {
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
public function scopeWomen($query)
{
return $query->whereGender('W');
}
}
$users = User::popular()->women()->orderBy('created_at')->get();
Eloquent #scopes in Laravel Docs
TL;DR. You can get around this by expressing your queries as MyModel::query()->find(10); instead of MyModel::find(10);.
To the best of my knowledge, starting PhpStorm 2017.2 code inspection fails for methods such as MyModel::where(), MyModel::find(), etc (check this thread), and this could get quite annoying.
One (elegant) way to get around this is to explicitly call ::query() wherever it makes sense to. This will let you benefit from free auto-completion and a nice formatting/indentating for your queries.
Examples
BAD
Snippet where inspection complains about static method calls
// static call complaint
$myModel = MyModel::find(10);
// another poorly formatted query with code inspection complaints
$myFilteredModels = MyModel::where('is_foo', true)
->where('is_bar', false)
->get();
GOOD
Well formatted code with no complaints
// no complaint
$myModel = MyModel::query()->find(10);
// a nicely formatted and indented query with no complaints
$myFilteredModels = MyModel::query()
->where('is_foo', true)
->where('is_bar', false)
->get();
Just in case this helps someone, I was getting this error because I completely missed the stated fact that the scope prefix must not be used when calling a local scope. So if you defined a local scope in your model like this:
public function scopeRecentFirst($query)
{
return $query->orderBy('updated_at', 'desc');
}
You should call it like:
$CurrentUsers = \App\Models\Users::recentFirst()->get();
Note that the prefix scope is not present in the call.
Solution to the original question
You called a non-static method statically. To make a public function static in the model, would look like this:
public static function {
}
In General:
Post::get()
In this particular instance:
Post::take(2)->get()
One thing to be careful of, when defining relationships and scope, that I had an issue with that caused a 'non-static method should not be called statically' error is when they are named the same, for example:
public function category(){
return $this->belongsTo('App\Category');
}
public function scopeCategory(){
return $query->where('category', 1);
}
When I do the following, I get the non-static error:
Event::category()->get();
The issue, is that Laravel is using my relationship method called category, rather than my category scope (scopeCategory). This can be resolved by renaming the scope or the relationship. I chose to rename the relationship:
public function cat(){
return $this->belongsTo('App\Category', 'category_id');
}
Please observe that I defined the foreign key (category_id) because otherwise Laravel would have looked for cat_id instead, and it wouldn't have found it, as I had defined it as category_id in the database.
You can give like this
public static function getAll()
{
return $posts = $this->all()->take(2)->get();
}
And when you call statically inside your controller function also..
I've literally just arrived at the answer in my case.
I'm creating a system that has implemented a create method, so I was getting this actual error because I was accessing the overridden version not the one from Eloquent.
Hope that help?
Check if you do not have declared the method getAll() in the model. That causes the controller to think that you are calling a non-static method.
For use the syntax like return Post::getAll(); you should have a magic function __callStatic in your class where handle all static calls:
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}

How to copy a bsoncxx::builder::basic::document to another?

Is there any way to safely copy a bsoncxx document to another.
In following code I am not able to do that
class DocClass
{
private:
bsoncxx::builder::basic::document m_doc;
public:
bsoncxx::builder::basic::document& copy(bsoncxx::builder::basic::document& obj)
{
obj = m_doc; //Not allowed
//Error C2280 attempting to reference a deleted function
}
};
There should not be any harm to the object even after copy.
Please help.
Thanks,
Shibin
If you want to copy a bsoncxx::document::value, you can construct a new one from its view:
bsoncxx::document::value foo = ...;
bsoncxx::document::value bar{foo.view()};
bsoncxx::builder::basic::document is only movable, not copyable. However, you can get view to the underlying document from the builder with the view() method, which might be able to help you depending on your use cases. You'll still only be able to extract from the builder once though, so you'll have to rely on constructing a second document::value if you need more than one.

MongoDB C# - update using custom strongly-typed objects not allowed?

I am trying to perform an update using strongly-typed objects. For example,
public void setAppointmentPrefs(string UserName, IEnumerable<AppointmentInfo> info)
{
var query = new QueryDocument {{ "ProviderId", UserName}};
var update = Update.Set("Prefs",prefs); // prefs.toList() gives same error
// providerprefs initialized in constructor
providerprefs.Update(query, update);
}
I receive a compiler error saying:Error 14 The best overloaded method match for 'MongoDB.Driver.Builders.Update.Set(string, MongoDB.Bson.BsonValue)' has some invalid arguments
Obviously the Mongo driver will not let me update based on my own object (whether as IEnumerable or prefs.toList()), which seems a contrast from the way it permits me to insert or query with custom objects. Surely I am missing something obvious that would permit me to avoid deserializing, weakly typing then creating a generic BsonDocument!! TIA.
You can do an Update based on your own types! Have you tried using the typed Query and Update builders?
Try something like this:
var query = Query<AppointmentInfo>.EQ(i => i.ProviderId, userName);
var update = Update<AppointmentInfo>.Set(i => i.Prefs, info.Prefs);
Not sure I got the types and everything write from your partial code, but that should give you the general idea.
Let me know if you have any further questions.
I know this has been answered but I for one don't fully understand Roberts answer.
All I did is call the "ToBsonDocument()" method for it to except the object as a parameter
So:
customObject.ToBsonDocument()
If you have an array of objects inside a document:
var query = Query.EQ("_id", ObjectId.Parse(id.ToString()));
var update = Update.Push("ArrayOfObjects", customObject.ToBsonDocument());
collection.Update(query, update);

EF1: Filtering derived types of entity class using .OfType<> by passing a string value

I have a situation where I'm trying to filter a LINQ select using a derived sub class.
ctx.BaseEntity.OfType<SubClass>() - this works fine.
However I'd like to do this using a string value instead. I've come across a performance barrier when I have lots (>20) Sub Classes and selecting an Entity without using OfType just isn't an option. I have a generic UI that renders from the base class, so I don't know what Class Type will be returned at compile time.
So what I'd like to do is this:
Perform a projected Select where I
return just the SubClassType from
the database
Perform a second select
using this value as the OfType to
only select the relevant related
entity from the database (No mass
unions generated)
int id = 1;
var classType = (from c in ctx.BaseClass.Include("ClassType")
where c.id == id
select new
{
c.ClassType.TypeName
}).First();
BaseClass caseQuery = ctx.BaseClass.OfType<classType.TypeName>()
.Include("ClassType")
.Include("ChildEntity1")
.Include("ChildEntity2")
.Where(x => x.id== id);
But obviously this won't work because OfType requires a Type and not a string.
Any ideas on how I can achieve this?
Update:
As a side note to the original question, it turns out that the moment you project a query that uses a Navigation Property - it builds the monster SQL too, so I've ended up using a stored procedure to populate my ClassType entity from the BaseClass Id.
So I've just got it to work using eSQL, which I'd never used before. I've posted the code here just in case it helps someone. Has anyone else got a more strongly typed solution they can think of?
BaseClass caseQuery = ctx.BaseClass.CreateQuery<BaseClass>("SELECT VALUE c FROM OFTYPE(Entities.[BaseClass],namespace.[" + classType.TypeName + "]) as c")
.Include("ClassType")
.Include("ChildEntity1")
.Include("ChildEntity2")
.Where(x => x.id== id).FirstOrDefault();
To answer the headline question about calling OfType with a string / runtime type, you can do the following:
// Get the type, assuming the derived type is defined in the same assembly
// as the base class and you have the type name as a string
var typeToFilter = typeof(BaseClass)
.Assembly
.GetType("Namespace." + derivedTypeName);
// The use reflection to get the OfType method and call it directly
MethodInfo ofType = typeof(Queryable).GetMethod("OfType");
MethodInfo ofTypeGeneric = method.MakeGenericMethod(new Type[] { typeToFilter });
var result = (IQueryable<Equipment>)generic.Invoke(null, new object[] { equipment });
Combine this with your stored procedure to get the class name and you (should?) avoid the massive join - I don't have table-per-type implementation to play with so I can't test.

How does the session state work in MVC 2.0?

I have a controller that stores various info (Ie. FormID, QuestionAnswerList, etc). Currently I am storing them in the Controller.Session and it works fine.
I wanted to break out some logic into a separate class (Ie. RulesController), where I could perform certain checks, etc, but when I try and reference the Session there, it is null. It's clear that the Session remains valid only within the context of the specific controller, but what is everyone doing regarding this?
I would imagine this is pretty common, you want to share certain "global" variables within the different controllers, what is best practice?
Here is a portion of my code:
In my BaseController class:
public List<QuestionAnswer> QuestionAnswers
{
get
{
if (Session["QuestionAnswers"] == null)
{
List<QuestionAnswer> qAnswers = qaRepository.GetQuestionAnswers(CurrentSection, UserSmartFormID);
Session["QuestionAnswers"] = qAnswers;
return qAnswers;
}
else
{
return (List<QuestionAnswer>)Session["QuestionAnswers"];
}
}
set
{
Session["QuestionAnswers"] = value;
}
}
In my first Controller (derived from BaseController):
QuestionAnswers = qaRepository.GetQuestionAnswers(CurrentSection, UserSmartFormID);
I stepped through the code and the above statement executes fine, setting the Session["QuestionAnswers"], but then when I try to get from another controller below, the Session["QuestionAnswers"] is null!
My second controller (also derived from BaseController):
List<QuestionAnswer> currentList = (List<QuestionAnswer>)QuestionAnswers;
The above line fails! It looks like the Session object itself is null (not just Session["QuestionAnswers"])
does it make a difference if you retrieve your session using
HttpContext.Current.Session("mySpecialSession") ''# note this is VB, not C#
I believe TempData will solve your problem, it operates with in the session and persists across multiple requests, however by default it will clear the stored data once you access it again, if that's a problem you can tell it to keep the info with the newly added Keep() function.
So in your case:
...
TempData["QuestionAnswers"] = qAnswers;
...
There's much more info at:
http://weblogs.asp.net/jacqueseloff/archive/2009/11/17/tempdata-improvements.aspx
Where are you accessing the session in the second controller? The session object is not available in the constructor because it is injected later on in the lifecycle.
Ok, finally got it working, although a bit kludgy. I found the solution from another related SO post.
I added the following to my BaseController:
public new HttpContextBase HttpContext
{
get
{
HttpContextWrapper context =
new HttpContextWrapper(System.Web.HttpContext.Current);
return (HttpContextBase)context;
}
}
Then set/retrieved my Session variables using HttpContext.Session and works fine!