LINQ to MongoDB: .Any with a Predicate - linq

I have a Collection in MongoDB of S documents. Each S has a collection of UserPermission objects, each of which have a UserId property. I want to select all the S documents that have a UserPermission with a certain UserId:
return collection.Where(s => s.UserPermissions.Any(up => up.UserId == userIdString)).ToList();
I get an error telling me that .Any with a predicate is not supported. The MongoDB docs say: "You can usually rewrite such a query by putting an equivalent where clause before the projection (in which case you can drop the projection)."
What does that mean? Any idea how I would change my query to get around this limitation?
Here's a full example. You can see I've tried 2 different queries and neither is supported:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace MongoSample
{
class Program
{
static void Main(string[] args)
{
App app1 = new App() { Name = "App1", Users = new List<User>()
{
new User() { UserName = "Chris" } }
};
App app2 = new App() { Name = "App2", Users = new List<User>()
{
new User() { UserName = "Chris" },
new User() { UserName = "Carlos" } }
};
MongoServer server = MongoServer.Create();
MongoDatabase database = server.GetDatabase("test");
MongoCollection appCollection = database.GetCollection("app");
appCollection.Insert(app1);
appCollection.Insert(app2);
// Throws "Any with a predicate not supported" error
//var query = appCollection.AsQueryable<App>()
// .Where(a => a.Users.Any(u => u.UserName == "Carlos"));
// Throws "Unsupported Where Clause" error.
var query = appCollection.AsQueryable<App>()
.Where(a => a.Users.Where(u => u.UserName == "Carlos").Any());
foreach (App loadedApp in query)
{
Console.WriteLine(loadedApp.ToJson());
}
Console.ReadLine();
}
}
class App
{
public string Name { get; set; }
public List<User> Users { get; set; }
}
class User
{
public string UserName { get; set; }
}
}

Any() without a predicate is supported, so you can do:
collection.Where(s => s.UserPermissions
.Where(up => up.UserId == userIdString).Any() )
(this is the "equivalent where clause" put before the Any)

Related

How could I read one to many linked table using link with Entity Framework?

I am new to Entity Framework and Linq (Visual Studio 2017 - EF 5.0) . Currently, I could read tables without issue but wonder how could I read a linked table.
My current functions do it but sure there is a simple way than two step reading that I have developed.
public override List<CartItem> GetMyCartOrderItems(int UserID)
{
try
{
using (foodorderingdbEntities oMConnection = new foodorderingdbEntities())
{
var oCart = oMConnection.carts.SingleOrDefault(p => p.USER_ID == UserID);
if (oCartItems != null)
{
int CartID = oCart.CART_ID;
var oCartItems = oMConnection.cart_item.Where(p => p.CART_ITEM_CART_ID == CartID);
if (oCartItems != null)
{
List<CartItem> oRecList = new List<CartItem>();
foreach (cart_item oDBrec in oCartItems)
{
CartItem oRec = new CartItem();
oRec.CartID = oDBrec.CART_ITEM_ID;
oRec.CartItemID = oDBrec.CART_ITEM_CART_ID;
oRec.DateTime = oDBrec.CART_ITEM_ADDED_DATE_TIME;
oRec.SystemComments = oDBrec.CART_ITEM_SYSTEM_COMMENTS;
oRecList.Add(oRec);
}
return oRecList;
}
else { return null; }
}
else { return null; }
}
}
catch (Exception ex)
{
//IBLogger.Write(LOG_OPTION.ERROR, "File : MHCMySQLDataConection.cs, Method : GetPatientByID(1), Exception Occured :" + ex.Message + Environment.NewLine + "Trace :" + ex.StackTrace);
return null;
}
}
You could see that I get Cart ID from Carts table using UserID and then I use the CartID retrieve cart Items from Cart_Item table. Cart_Item_Cart_ID is a foreign key in cart_item table. (This is a one to many table)
This is what I am thinking but obviously does not work.
List<cart_item> oCartItems = oMConnection.carts.SingleOrDefault(c => c.USER_ID == UserID).cart_item.Where(p => p.CART_ITEM_CART_ID = c.CART_ID).ToList<cart_item>();
Any help ?
My entity relation
public partial class cart
{
public cart()
{
this.cart_item = new HashSet<cart_item>();
}
public int CART_ID { get; set; }
public int USER_ID { get; set; }
public decimal ORDER_TOTAL_COST { get; set; }
public virtual ICollection<cart_item> cart_item { get; set; }
public virtual user user { get; set; }
}
Because your query has multiple levels of one to many relationships, and you just want the cart_items, it's easier to go the other way like this:
var oCart = oMConnection.cart_item
.Where(c=>c.cart.user.USER_ID == UserID);
Going the way you did should have worked as well, but you needed to use SelectMany instead of select like this:
var oCartItems = oMConnection.carts
.Where(c=>c.USER_ID==UserID)
.SelectMany(c=>c.cart_item);

Telerik Data Access - Argument Null Exception (ConverterName)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VideoWebsite.DataAccess;
namespace VideoWebsite.Models
{
public class DatabaseModel
{
public int DatabaseID { get; set; }
public string DatabaseName { get; set; }
}
public class DatabaseModelAccess
{
public static IEnumerable<DatabaseModel> All()
{
string queryString = #"SELECT 1 as DatabaseID, 'test' as DatabaseName";
using (EntitiesModelA2 dbContext2 = new EntitiesModelA2())
{
IEnumerable<DatabaseModel> result = dbContext2.ExecuteQuery<DatabaseModel>(queryString); //Exception occurs here
return result;
}
}
}
}
I am trying to materialize the non persistent type.
I am using the postgres database to generate the EntitiesModelA2.
From Telerik Data Access perspective, the error appears because the query does not have a FROM clause (it does not comply to the basic syntax for SELECT statements).
You can execute it like this:
Option 1
using (EntitiesModelA2 dbContext = new EntitiesModelA2())
{
using (IDbConnection connect = dbContext.Connection)
{
using (IDbCommand command = connect.CreateCommand())
{
command.CommandText = "SELECT 1 as DatabaseID, 'test' as DatabaseName FROM ExistingTableName";
using (IDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
var result = new DatabaseModel();
result.DatabaseID = reader.GetInt32(0);
result.DatabaseName = reader.GetString(1);
//do something with the result
}
}
}
}
}
Option 2
using (EntitiesModelA2 dbContext = new EntitiesModelA2())
{
var result = dbContext.ExecuteQuery<DatabaseModel>("SELECT 1 as DatabaseID, 'test' as DatabaseName FROM ExistingTableName");
}

Integrating AspNet.WebApi with AspNet.SignalR

I want to integrate Microsoft.AspNet.SignalR version="2.1.2" with Microsoft.AspNet.WebApi version="5.2.2" so that the API can communicate in REAL-TIME. I found one VERY NICE SAMPLE that implements/works exactly the same way I want, but the sample uses jquery.signalR-0.5.0.js. Some of the earlier implementations have changed, and so far here is what I have done in a failed effort to upgrade the solution to use the latest signalr, asp.net web api and owin.
I left the Hub as it is
using SignalR.Hubs;
namespace NdcDemo.Hubs
{
// This hub has no inbound APIs, since all inbound communication is done
// via the HTTP API. It's here for clients which want to get continuous
// notification of changes to the ToDo database.
[HubName("todo")]
public class ToDoHub : Hub { }
}
I also left the ApiControllerWithHub class as it is
using System;
using System.Web.Http;
using SignalR;
using SignalR.Hubs;
namespace NdcDemo.Controllers
{
public abstract class ApiControllerWithHub<THub> : ApiController
where THub : IHub
{
Lazy<IHubContext> hub = new Lazy<IHubContext>(
() => GlobalHost.ConnectionManager.GetHubContext<THub>()
);
protected IHubContext Hub
{
get { return hub.Value; }
}
}
}
For the ToDoController, I changed the
Hub.Clients.addItem(item), Hub.Clients.updateItem(toUpdate),
Hub.Clients.deleteItem(id)
to
Hub.Clients.All.addItem(item), Hub.Clients.All.updateItem(toUpdate),
Hub.Clients.All.deleteItem(id)
and this is now the full ToDoController class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Web.Http;
using NdcDemo.Hubs;
using NdcDemo.Models;
namespace NdcDemo.Controllers
{
[InvalidModelStateFilter]
public class ToDoController : ApiControllerWithHub<ToDoHub>
{
private static List<ToDoItem> db = new List<ToDoItem>
{
new ToDoItem { ID = 0, Title = "Do a silly demo on-stage at NDC" },
new ToDoItem { ID = 1, Title = "Wash the car" },
new ToDoItem { ID = 2, Title = "Get a haircut", Finished = true }
};
private static int lastId = db.Max(tdi => tdi.ID);
public IEnumerable<ToDoItem> GetToDoItems()
{
lock (db)
return db.ToArray();
}
public ToDoItem GetToDoItem(int id)
{
lock (db)
{
var item = db.SingleOrDefault(i => i.ID == id);
if (item == null)
throw new HttpResponseException(
Request.CreateResponse(HttpStatusCode.NotFound)
);
return item;
}
}
public HttpResponseMessage PostNewToDoItem(ToDoItem item)
{
lock (db)
{
// Add item to the "database"
item.ID = Interlocked.Increment(ref lastId);
db.Add(item);
// Notify the connected clients
Hub.Clients.addItem(item);
// Return the new item, inside a 201 response
var response = Request.CreateResponse(HttpStatusCode.Created, item);
string link = Url.Link("apiRoute", new { controller = "todo", id = item.ID });
response.Headers.Location = new Uri(link);
return response;
}
}
public ToDoItem PutUpdatedToDoItem(int id, ToDoItem item)
{
lock (db)
{
// Find the existing item
var toUpdate = db.SingleOrDefault(i => i.ID == id);
if (toUpdate == null)
throw new HttpResponseException(
Request.CreateResponse(HttpStatusCode.NotFound)
);
// Update the editable fields and save back to the "database"
toUpdate.Title = item.Title;
toUpdate.Finished = item.Finished;
// Notify the connected clients
Hub.Clients.updateItem(toUpdate);
// Return the updated item
return toUpdate;
}
}
public HttpResponseMessage DeleteToDoItem(int id)
{
lock (db)
{
int removeCount = db.RemoveAll(i => i.ID == id);
if (removeCount <= 0)
return Request.CreateResponse(HttpStatusCode.NotFound);
// Notify the connected clients
Hub.Clients.deleteItem(id);
return Request.CreateResponse(HttpStatusCode.OK);
}
}
}
}
And then I put app.MapSignalR();
But the demo doesn't work...the API doesn't contact clients...Where am I going wrong?
I would still appreciate any more simpler recommendations based on this solution.
Solution from OP.
Answer:
After a a cup of camomile tea, i found out that the clients had to include the KEYWORD CLIENT before the dynamic methods in Todo.js... So, here is what that needs to be modified so that the sample works
hub.client.addItem = function (item) {
alert("i just received something...");
viewModel.add(item.ID, item.Title, item.Finished);
};
hub.client.deleteItem = function (id) {
viewModel.remove(id);
};
hub.client.updateItem = function (item) {
viewModel.update(item.ID, item.Title, item.Finished);
};
And it works!

LINQ to Entity: How to cast a complex type to a DTO

Background:
I have created a function import which is available in my context object as GetJournalViewItemsQuery()
The function import returns a complex type called JournalViewItem.
Now when I try to load the JournalViewItem into my application DTO called JournalEntry I come up with error:
Error 7 Cannot implicitly convert type 'MyApp.Infrastructure.Models.JournalEntry' to 'MyApp.SqlData.JournalViewItem'
This is the code:
var journalEntry = Context.GetJournalViewItemsQuery()
.Where(i => i.JournalItemId == _journalEntryId)
.Select(x => new JournalEntry(x.JournalItemId,
x.HeaderText,x.JournalText, x.LastUpdatedOn,
x.JournalItemTypeId)).Single();
The error happens at the "new JournalEntry" line.
My question: How can I cast the JournalViewItem complex type to my DTO ?
Thank you
After #JanR suggestion I still have the same problem. The modified code is:
var journalEntry = Context.GetJournalViewItemsQuery()
.Where(i => i.JournalItemId == _journalEntryId)
.Select(x => new JournalEntry
{
JournalEntryNumber = x.JournalItemId,
HeaderText = x.HeaderText,
BodyText = x.JournalText,
LastUpdatedOn = x.LastUpdatedOn,
JournalEntryType = x.JournalItemTypeId
}).Single();
I found out the reason for my problem. I failed to mention (my apologies) that I'm working off generated code from WCF RIA Domain Services for Silverlight application. As such the Context.GetJournalViewItemsQuery() needs to be executed and THEN I can query the results on my callback method using the LINQ expression that #Chuck.Net and JanR have suggested.
Here's the working code to those who might be interested:
public IList<JournalEntryHeader> GetJournalEntryHeaders()
{
PerformQuery<JournalViewItem>(Context.GetJournalViewItemsQuery(), GetJournalEntryHeadersFromDbComplete);
return _journalHeaders;
}
void PerformJournalEntryHeadersQuery(EntityQuery<JournalViewItem> qry,
EventHandler<EntityResultsArgs<JournalViewItem>> evt)
{
Context.Load<JournalViewItem>(qry, r =>
{
if (evt != null)
{
try
{
if (r.HasError)
{
evt(this, new EntityResultsArgs<JournalViewItem>(r.Error));
}
else if (r.Entities.Count() > 0)
{
evt(this, new EntityResultsArgs<JournalViewItem>(Context.JournalViewItems));
}
else if (r.Entities.Count() == 0 && _currentJournalItemsPage > 0)
{
GetPrevPageJournalEntryHeadersAsync();
}
}
catch (Exception ex)
{
evt(this, new EntityResultsArgs<JournalViewItem>(ex));
}
}
}, null);
}
void GetJournalEntryHeadersFromDbComplete(object sender, EntityResultsArgs<JournalViewItem> e)
{
if (e.Error != null)
{
string errMsg = e.Error.Message;
}
else
{
_journalHeaders = e.Results
.Select(
x => new JournalEntryHeader(x.JournalItemId,
x.ProjectName,
x.TopicName,
x.HeaderText,
x.EntryTypeName,
x.LastUpdatedOn)).ToList();
GetJournalEntryHeadersComplete(this, new JournalEntryHeaderItemsEventArgs(_journalHeaders));
}
}
What you need to do is the following, in the new JournalEntry() function you will need to set all the properties to the JournalViewItem object.
var journalEntry = Context.GetJournalViewItemsQuery()
.Where(i => i.JournalItemId == _journalEntryId)
.Select(x => new JournalEntry {
JournalEntryId = x.JournalItemId,
HeaderText = x.HeaderText,
JournalText = x.JournalText
//etc
}).Single();
I am just guessing the actual property names here as I am not familiar with what the JounralEntry object looks like.
EDIT: added {}
I created a ConsoleApp to test #JanR answer. It seems to be working correctly.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackOverFlowConsoleApplication
{
class Program
{
static void Main(string[] args)
{
List<JournalViewItem> JournalViewItems = new List<JournalViewItem>()
{
new JournalViewItem(){JournalItemId =1, HeaderText="HeaderText", JournalText="JournalText", LastUpdatedOn= DateTime.Today, JournalItemTypeId=1},
};
int _journalEntryId = 1;
var journalEntry = JournalViewItems
.Where(i => i.JournalItemId == _journalEntryId)
.Select(x => new JournalEntry
{
JournalEntryNumber = x.JournalItemId,
HeaderText = x.HeaderText,
BodyText = x.JournalText,
LastUpdatedOn = x.LastUpdatedOn,
JournalEntryType = x.JournalItemTypeId
}).Single();
}
class JournalViewItem
{
public int JournalItemId { get; set; }
public string HeaderText { get; set; }
public string JournalText { get; set; }
public DateTime LastUpdatedOn { get; set; }
public int JournalItemTypeId { get; set; }
}
class JournalEntry
{
public int JournalEntryNumber { get; set; }
public string HeaderText { get; set; }
public string BodyText { get; set; }
public DateTime LastUpdatedOn { get; set; }
public int JournalEntryType { get; set; }
}
}
}
I looked into complx type a little bit. I tried the following code in my own project and was able to reproduce the error you mentioned:
var result = (from y in CS.PSMBIPMTTXLOGs select new PSMBIPMTCONTROL(){MBI_PMT_TX_ID = y.MBI_PMT_TX_ID}).ToList();
But when I changed it to return anonymous type, it worked:
var result = (from y in CS.PSMBIPMTTXLOGs select new {MBI_PMT_TX_ID = y.MBI_PMT_TX_ID}).ToList();
You mentioned that you have even tried creating an anonymous type instead of newing it into my DTO, but it still complains. Can you post your code for returning an anonymous type and the error message it gives? Thank you.
I found out the reason for my problem. I failed to mention (my apologies) that I'm working off generated code from WCF RIA Domain Services for Silverlight application. As such the Context.GetJournalViewItemsQuery() needs to be executed and THEN I can query the results on my callback method using the LINQ expression that #Chuck.Net and JanR have suggested.
You'll find the answer in the original post where I entered the question.

Is there a way to do "AND" in Net SQL AzMan instead of "OR"?

All of the settings in Net SQL AzMan seem to be "OR" based.
For example:
If you add 3 (Authorized) Application Groups to an operation, a user needs to be in the first OR the second OR the third to have permissions for the operation.
I am looking for a way to say the user needs to be in (the first AND the second) OR (the first AND the third).
Is there a way to do that?
Reason Why:
We have users that snowball permissions as they move from department to department. I want to setup one role per Active Directory Departement ("the first" in my example above). If I can get the above logic working then when the user changes departments they will lose the permissions from their former department (even if their boss is lazy and does not get AzMan updated).
If I can't get this working in AzMan, then I can have my apps do it. But it would be so much easier at the AzMan level.
You could do this with a BizRule on the operation. The code for it is a bit overkill, but this should work with minimal modifications.
using System;
using System.Security.Principal;
using System.IO;
using System.Data;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Text;
using NetSqlAzMan;
using NetSqlAzMan.Interfaces;
using System.Security.Principal;
using System.Reflection;
namespace APPLICATION.BizRules
{
public sealed class BizRule : IAzManBizRule
{
public BizRule()
{ }
public bool Execute(Hashtable contextParameters, IAzManSid identity, IAzManItem ownerItem, ref AuthorizationType authorizationType)
{
string sqlConnectionString = "data source=DATABASE_FQN;initial catalog=DATABASE;Integrated Security=false;User Id=USER_NAME;Password=PASSWORD";
IAzManStorage storage = new SqlAzManStorage(sqlConnectionString);
try
{
bool authorized = false;
if (identity.StringValue.StartsWith("S"))
{
//this is a little over kill but there is no way to reference standard .net libraries in NetSqlAzMan
Assembly asm = Assembly.Load(#"System.DirectoryServices.AccountManagement, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=b77a5c561934e089");
System.Type userPrincipalType = asm.GetType("System.DirectoryServices.AccountManagement.UserPrincipal");
System.Type principalContextType = asm.GetType("System.DirectoryServices.AccountManagement.PrincipalContext");
System.Type contextTypeType = asm.GetType("System.DirectoryServices.AccountManagement.ContextType");
System.Type identityTypeType = asm.GetType("System.DirectoryServices.AccountManagement.IdentityType");
Object principalContext = Activator.CreateInstance(principalContextType, new object[] { Enum.ToObject(contextTypeType, 1), "DENALLIX" });
MethodInfo methodInfo = userPrincipalType.GetMethod("FindByIdentity", new Type[] { principalContextType, identityTypeType, typeof(string) });
Object userPrincipal = methodInfo.Invoke(null, new object[] { principalContext, Enum.ToObject(identityTypeType, 4), identity.StringValue });
string userPrincipalName = userPrincipal.GetType().GetProperty("UserPrincipalName").GetValue(userPrincipal, null).ToString();
WindowsIdentity user = new WindowsIdentity(userPrincipalName);
authorized = (checkRoleAuthorization(storage, "GROUP1", user) && checkRoleAuthorization(storage, "GROUP2", user)) || checkRoleAuthorization(storage, "GROUP3", user);
}
else
{
AzManUser user = new AzManUser(identity);
authorized = (checkRoleAuthorization(storage, "GROUP1", user) && checkRoleAuthorization(storage, "GROUP2", user)) || checkRoleAuthorization(storage, "GROUP3", user);
}
return authorized;
}
catch (SqlAzManException ex)
{
return false;
}
}
private bool checkRoleAuthorization(IAzManStorage storage, string roleName, object user)
{
AuthorizationType auth = AuthorizationType.Deny;
if (user is WindowsIdentity)
{
auth = storage.CheckAccess("MY STORE", "MY APPLICATION", roleName, (WindowsIdentity)user, DateTime.Now, true);
}
else
{
auth = storage.CheckAccess("MY STORE", "MY APPLICATION", roleName, (IAzManDBUser)user, DateTime.Now, true);
}
return auth == AuthorizationType.Allow || auth == AuthorizationType.AllowWithDelegation;
}
}
public partial class AzManUser : IAzManDBUser
{
private Dictionary<string, object> _customColumns = new Dictionary<string, object>();
private IAzManSid _sid;
private string _username;
public AzManUser(string username, string sid)
{
_username = username;
_sid = new NetSqlAzMan.SqlAzManSID(sid);
}
public AzManUser(string sid)
{
_username = string.Empty;
_sid = new NetSqlAzMan.SqlAzManSID(sid);
}
public AzManUser(IAzManSid sid)
{
_username = string.Empty;
_sid = sid;
}
public Dictionary<string, object> CustomColumns
{
get { return _customColumns; }
}
public IAzManSid CustomSid
{
get
{
return _sid;
}
}
public string UserName
{
get { return _username; }
}
}
}

Resources