Add multiple included dynamically - entity-framework-6

I put together some helpers that will allow me to register includes by type. Looks like this:
Dictionary<Type, LambdaExpression[]> includes =
new Dictionary<Type, LambdaExpression[]>();
I register includes like this:
public void Add<T>(params Expression<Func<T, object>>[] includes)
{
this.includes.Add(typeof(T), includes);
}
Add<Customer>(e =>
e.Address.City.Province.Country,
e.Products.Select(p => p.Category));
Notice there are two includes for Customer. I then have this method that gets includes by type:
DbSet<T> entitySet = null;
void LoadIncludes()
{
var includes = Includes.Instance.Get<T>().DirectCast<Expression<Func<T, object>>[]>();
if (includes != null)
{
foreach (var include in includes)
{
entitySet.Include(include).Load();
}
}
}
When getting my entity, I do this:
public T GetById(int id)
{
LoadIncludes();
return entitySet.SingleOrDefault(x => x.Id == id);
}
It works well, but it's so slow, and it's because of the .Load() method I am calling in LoadIncludes(). Is there a faster way to do what I want to do here?

You shouldn't call Load, but build and use a Queryable<T> with Include chain.
Replace LoadIncludes with private function:
private IQueryable<T> GetEntitySet()
{
var set = entitySet.AsQueryable();
var includes = Includes.Instance.Get<T>().DirectCast<Expression<Func<T, object>>[]>();
if (includes != null)
{
foreach (var include in includes)
{
set = set.Include(include);
}
}
return set;
}
and use it as follows:
public T GetById(int id)
{
return GetEntitySet().SingleOrDefault(x => x.Id == id);
}

Related

Hide a header from being displayed in Swagger Swashbuckle

As I already have a common authorization added how do I remove separate(authorization header) from each and every API as shown in the image link below?
swaggerHub_Link
I figured it out:
You can simply create a custom attibute and an operation filter inhering from Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter in order to hide the header from being displayed in swagger.json
public class OpenApiHeaderIgnoreAttribute : System.Attribute
{
}
class name should end with the name of the base class.
public class OpenApiHeaderIgnoreFilter : Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter
{
public void Apply(Microsoft.OpenApi.Models.OpenApiOperation operation, Swashbuckle.AspNetCore.SwaggerGen.OperationFilterContext context)
{
if (operation == null || context == null || context.ApiDescription?.ParameterDescriptions == null)
return;
var parametersToHide = context.ApiDescription.ParameterDescriptions
.Where(parameterDescription => ParameterHasIgnoreAttribute(parameterDescription))
.ToList();
if (parametersToHide.Count == 0)
return;
foreach (var parameterToHide in parametersToHide)
{
var parameter = operation.Parameters.FirstOrDefault(parameter => string.Equals(parameter.Name, parameterToHide.Name, System.StringComparison.Ordinal));
if (parameter != null)
operation.Parameters.Remove(parameter);
}
}
private static bool ParameterHasIgnoreAttribute(Microsoft.AspNetCore.Mvc.ApiExplorer.ApiParameterDescription parameterDescription)
{
if (parameterDescription.ModelMetadata is Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata metadata)
{
return metadata.Attributes.ParameterAttributes.Any(attribute => attribute.GetType() == typeof(OpenApiParameterIgnoreAttribute));
}
return false;
}
}
and set this filter class in your startup class configureServices method as below:
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.OperationFilter<OpenApiHeaderIgnoreFilter>();
}
}
and also update your API method in controller as below:
[EnableQuery]
[SwaggerOperation(Tags = new[] { "Odata" })]
public async Task<IActionResult> Get([OpenApiHeaderIgnore] [FromHeader(Name = ClaimNames.AccessToken)] string token)
{
// code
}

how to prevent script injections centrally for .net core mvc application

I just need some opinions on implementing a logic to centrally check if there is no scripts added to the inputs.
I am planning to use antiXSS (Sanitizer.GetSafeHtmlFragment("value")) and check the output if it is null, meaning it could contain script and handle the error.
I could come up with an logic to through the model properties and check the value and if there is anything suspicious throws an error.
I wonder if there is a better way of handling this injections for all input fields in one go rather than adding validations for each field.
Lets say if I have a model like:
public class Login {
public string Email {get; set;}
public string Password {get; set;}
}
Can I just add some sort of filtering to check if non of the inputs contain any scripts before hitting the action rather than adding some attributes to the model or validation express and then do html encode individually for each field and then throw an error.
I want something very at top so I don't go through each actions or model and make some changes.
I used filter action and added such a code to check for string type of any model in the request and encode it. It works perfectly fine for us.
public static class HttpEncode
{
public static void ParseProperties(this object model)
{
if (model == null) return;
if (IsPropertyArrayOrList(model.GetType()))
{
ParsePropertiesOfList(model);
}
else
{
GetAllProperties(model).ForEach(t => EncodeField(t, model));
}
}
private static void ParsePropertiesOfList(object model)
{
foreach (var item in (IEnumerable) model)
{
ParseProperties(item);
}
}
private static List<PropertyInfo> GetAllProperties(object value) => value?.GetType()?.GetProperties()?.ToList();
private static void EncodeField(PropertyInfo p, object arg)
{
try
{
if (p.GetIndexParameters().Length != 0 || p.GetValue(arg) == null)
return;
if (IsUserDefinedClass(p.PropertyType) && p.CanWrite)
{
ParseProperties(p.GetValue(arg));
}
else if (IsPropertyArrayOrList(p.PropertyType) && p.CanWrite)
{
ParseArrayOrListProperty(p, arg);
}
else if (p.PropertyType == typeof(string) && p.CanWrite)
{
var encodedValue = HtmlEncode(p.GetValue(arg)?.ToString());
SetPropertyValue(p, arg, encodedValue);
}
}
catch (Exception ex)
{
// ignored
}
}
private static void ParseArrayOrListProperty(PropertyInfo p, object arg)
{
if (p.GetValue(arg) is string[] || p.GetValue(arg) is List<string>)
{
SetPropertyValueOfStaringArrayType(p, arg);
}
else
{
ParsePropertiesOfList(p.GetValue(arg));
}
}
private static void SetPropertyValueOfStaringArrayType(PropertyInfo propertyInfo, object arg)
{
if (propertyInfo.GetValue(arg) is string[] stringValue)
{
var result = new List<string>();
stringValue.ToList().ForEach(l => result.Add(HtmlEncode(l)));
SetPropertyValue(propertyInfo, arg, result.Any() ? result.ToArray() : null);
}
else if (propertyInfo.GetValue(arg) is List<string> listValue)
{
var result = new List<string>();
listValue.ForEach(l => result.Add(HtmlEncode(l)));
SetPropertyValue(propertyInfo, arg, result.Any() ? result : null);
}
}
private static bool IsUserDefinedClass(Type type) =>
type.IsClass &&
!type.FullName.StartsWith("System.");
private static bool IsPropertyArrayOrList(Type type) =>
type.IsArray && type.GetElementType() == typeof(string) ||
(type != typeof(string) && type.GetInterface(typeof(IEnumerable<>).FullName) != null);
private static void SetPropertyValue(PropertyInfo propertyInfo, object allValue, object value)
{
propertyInfo.SetValue(allValue, value);
}
private static string HtmlEncode(string value) => HttpUtility.HtmlEncode(value);
}
public class EncodeInputsActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
ProcessHtmlEncoding(context);
var resultContext = await next();
// do something after the action executes; resultContext.Result will be set
}
private static void ProcessHtmlEncoding(ActionExecutingContext context)
{
context.ActionArguments.ToList().ForEach(arg => { arg.Value.ParseProperties(); });
}
}

Changing the output DTO (return value) of the GetAll method in an AsyncCrudAppService

I'm using ABP's AsyncCrudAppService in my AppServices. Here's my interface:
public interface IAssetRequisitionAppService : IAsyncCrudAppService
<AssetRequisitionDto, Guid, GetAllInput, AssetRequisitionDto, AssetRequisitionDto, AssetRequisitionDetailsDto>
{ }
And the service:
public class AssetRequisitionAppService : AsyncCrudAppService
<AssetRequisition, AssetRequisitionDto, Guid, GetAllInput, AssetRequisitionDto, AssetRequisitionDto, AssetRequisitionDetailsDto>,
IAssetRequisitionAppService
{
public AssetRequisitionAppService(IRepository<AssetRequisition, Guid> repository) : base(repository)
{ }
}
Now, I believe all these standard CRUD methods will return the default type (which is AssetRequisitionDto in my case). But, what I want to do is to return a different type for Get() and GetAll() methods.
Get() should have a much more detailed DTO with subproperties of the Navigation props. But GetAll() should have a much less detailed one just to populate a table.
Is there a way to override the return types in some way?
Yes, there is some way.
// using Microsoft.AspNetCore.Mvc;
[ActionName(nameof(GetAll))]
public PagedResultRequestDto MyGetAll(PagedResultRequestDto input)
{
return input;
}
[NonAction]
public override Task<PagedResultDto<UserDto>> GetAll(PagedResultRequestDto input)
{
return base.GetAll(input);
}
Reference: https://github.com/aspnetboilerplate/aspnetboilerplate/issues/2859
Well I noticed I'll eventually need more complex filtering methods anyway. So, I created my custom types and methods.
First, I created my own GetAllInput derived from PagedAndSortedResultRequestDto. It's suited for most of my services, as I usually need to query the data related to employees and locations:
public class GetAllInput : PagedAndSortedResultRequestDto
{
public long? PersonId { get; set; }
public long? LocationId { get; set; }
public EEmployeeType? EmployeeType { get; set; }
}
After that I wrote a GetAll method for each of my services. They all return a PagedResultDto<> so I can use it's functionalities in presentation layer. Here's one example below:
//MovableAppService
public PagedResultDto<MovableLineDto> GetAllLinesRelatedToUser(GetAllInput input)
{
Logger.Info("Loading all movables related to current user");
IQueryable<Movable> queryable = null;
if (input.PersonId.HasValue)
{
if (input.EmployeeType == EEmployeeType.Recorder)
{
var recorder = _personRepository.GetAll()
.OfType<Employee>()
.FirstOrDefault(x => x.Id == input.PersonId);
var authorizedStorageIds = recorder.StoragesAuthorized.Select(y => y.Id);
queryable = _repository.GetAll()
.Where(x => authorizedStorageIds.Contains(x.StorageOfOriginId));
}
else if (input.EmployeeType == EEmployeeType.UnitManager)
{
var locationCodes = _locationRepository.GetAll()
.Where(x => x.ManagerInChargeId == input.PersonId)
.Select(x => x.Code);
foreach (var code in locationCodes)
{
queryable = _locationRepository.GetAll()
.Where(x => x.Code.StartsWith(code))
.SelectMany(x => x.AssignmentDocs)
.SelectMany(x => x.Movements)
.OfType<Assignment>()
.Where(x => x.TimeOfReturn == null)
.Select(x => x.Asset)
.OfType<Movable>();
queryable = queryable.Concat(queryable);
}
}
else if (input.TenantIdsOversee.Count() > 0)
{
var overseer = _personRepository.GetAll()
.OfType<Overseer>()
.FirstOrDefault(x => x.Id == input.PersonId);
var overseeingTenantIds = overseer.TenantsOversee.Select(y => y.Id);
queryable = _repository.GetAll()
.Where(x => overseeingTenantIds.Contains((int)x.TenantId));
}
else if (input.EmployeeType == EEmployeeType.Employee)
{
queryable = _personRepository.GetAll()
.OfType<Employee>()
.Where(x => x.Id == input.PersonId)
.SelectMany(x => x.AssignmentDocs)
.SelectMany(x => x.Movements)
.OfType<Assignment>()
.Where(x => x.TimeOfReturn == null)
.Select(x => x.Asset)
.OfType<Movable>();
}
}
var list = queryable.ToList()
.OrderBy(x => x.Category.Definition);
var items = _objectMapper.Map<IReadOnlyList<MovableLineDto>>(list);
return new PagedResultDto<MovableLineDto>(items.Count, items);
}
Btw, aaron's answer is probably valid for ASP.NET Core projects. But my project is in MVC EF6, so those annotations are not available for me.
Now I'm marking this as the answer but if there's a more elegant way, I'm happy to see and I'll change my mark then.

OData Client Request Pipeline not working in Odata V4

I am following the sample blog below to remove and add properties in request ODataEntry class.
http://blogs.msdn.com/b/odatateam/archive/2013/07/26/using-the-new-client-hooks-in-wcf-data-services-client.aspx
But even if the code works fine and adds and removes the properties correctly when I put breakpoint, all the entity properties goes to server un changed.
Only difference I see this I am using the OData V4 and new Ondata client to hook up.
My code looks below.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Client.Default;
namespace Client
{
using Client.MvcApplication1.Models;
using Microsoft.OData.Core;
internal class Program
{
private static void Main(string[] args)
{
Container container = new Container(new Uri("http://localhost:55000/api/"));
container.Configurations.RequestPipeline.OnEntryEnding(
w =>
{
w.Entry.RemoveProperties("Name");
});
Test test = new Test();
test.Name = "Foo";
CustomFields cs = new CustomFields { ServiceId = 3 };
cs.Foo1 = 2;
test.S_1 = cs;
container.AddToTests(test);
container.SaveChanges();
}
}
public static class Extensions
{
public static void RemoveProperties(this ODataEntry entry, params string[] propertyNames)
{
var properties = entry.Properties as List<ODataProperty>;
if (properties == null)
{
properties = new List<ODataProperty>(entry.Properties);
}
var propertiesToRemove = properties.Where(p => propertyNames.Any(rp => rp == p.Name));
foreach (var propertyToRemove in propertiesToRemove.ToArray())
{
properties.Remove(propertyToRemove);
}
entry.Properties = properties;
}
public static void AddProperties(this ODataEntry entry, params ODataProperty[] newProperties)
{
var properties = entry.Properties as List<ODataProperty>;
if (properties == null)
{
properties = new List<ODataProperty>(entry.Properties);
}
properties.AddRange(newProperties);
entry.Properties = properties;
}
}
}
If I change and start listening to RequestPipeline.OnEntryStarting I get the validation error that new property is not defined in owning entity. But as per code for Microsoft.OData.CLient this error should not occure as there is a check for IEdmStructuredType.IsOpen but still error occurs. So issue seems deep in how owningStructuredType is calculated. On my container I do see the correct edm model with entities marked as IsOpen = true.
Odata lib code which should pass but is failing
internal static IEdmProperty ValidatePropertyDefined(string propertyName, IEdmStructuredType owningStructuredType)
{
Debug.Assert(!string.IsNullOrEmpty(propertyName), "!string.IsNullOrEmpty(propertyName)");
if (owningStructuredType == null)
{
return null;
}
IEdmProperty property = owningStructuredType.FindProperty(propertyName);
// verify that the property is declared if the type is not an open type.
if (!owningStructuredType.IsOpen && property == null)
{
throw new ODataException(Strings.ValidationUtils_PropertyDoesNotExistOnType(propertyName, owningStructuredType.ODataFullName()));
}
return property;
}
Client code:
container.Configurations.RequestPipeline.OnEntryStarting(
w =>
{
w.Entry.RemoveProperties("Name");
w.Entry.AddProperties(new ODataProperty
{
Name = "NewProperty",
Value = 1
});
});
Error:
The property 'NewProperty' does not exist on type 'Client.MvcApplication1.Models.Test'. Make sure to only use property names that are defined by the type.
at Microsoft.OData.Core.WriterValidationUtils.ValidatePropertyDefined(String propertyName, IEdmStructuredType owningStructuredType)
at Microsoft.OData.Core.JsonLight.ODataJsonLightPropertySerializer.WriteProperty(ODataProperty property, IEdmStructuredType owningType, Boolean isTopLevel, Boolean allowStreamProperty, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties)
at Microsoft.OData.Core.JsonLight.ODataJsonLightPropertySerializer.WriteProperties(IEdmStructuredType owningType, IEnumerable`1 properties, Boolean isComplexValue, DuplicatePropertyNamesChecker duplicatePropertyNamesChecker, ProjectedPropertiesAnnotation projectedProperties)
at Microsoft.OData.Core.JsonLight.ODataJsonLightWriter.StartEntry(ODataEntry entry)
at Microsoft.OData.Core.ODataWriterCore.<>c__DisplayClass14.<WriteStartEntryImplementation>b__12()
at Microsoft.OData.Core.ODataWriterCore.InterceptException(Action action)
at Microsoft.OData.Core.ODataWriterCore.WriteStartEntryImplementation(ODataEntry entry)
at Microsoft.OData.Core.ODataWriterCore.WriteStart(ODataEntry entry)
at Microsoft.OData.Client.ODataWriterWrapper.WriteStart(ODataEntry entry, Object entity)
at Microsoft.OData.Client.Serializer.WriteEntry(EntityDescriptor entityDescriptor, IEnumerable`1 relatedLinks, ODataRequestMessageWrapper requestMessage)
at Microsoft.OData.Client.BaseSaveResult.CreateRequestData(EntityDescriptor entityDescriptor, ODataRequestMessageWrapper requestMessage)
at Microsoft.OData.Client.BaseSaveResult.CreateChangeData(Int32 index, ODataRequestMessageWrapper requestMessage)
at Microsoft.OData.Client.SaveResult.CreateNonBatchChangeData(Int32 index, ODataRequestMessageWrapper requestMessage)
at Microsoft.OData.Client.SaveResult.CreateNextChange()
I use partial classes defined on the client to add the extra properties that I need there. This allows me to put any logic in them as well as have property changed notification as well. I the use the following extension methods to remove those properties. I think I actually got the original code from the article that you linked.
public static class DbContextExtensions
{
public static void RemoveProperties(this ODataEntry entry, params string[] propertyNames)
{
var properties = entry.Properties as List<ODataProperty>;
if (properties == null)
{
properties = new List<ODataProperty>(entry.Properties);
}
var propertiesToRemove = properties.Where(p => propertyNames.Any(rp => rp == p.Name));
foreach (var propertyToRemove in propertiesToRemove.ToArray())
{
properties.Remove(propertyToRemove);
}
entry.Properties = properties;
}
public static DataServiceClientResponsePipelineConfiguration RemoveProperties<T>(this DataServiceClientResponsePipelineConfiguration responsePipeline, Func<string, Type> resolveType, params string[] propertiesToRemove)
{
return responsePipeline.OnEntryEnded((args) =>
{
Type resolvedType = resolveType(args.Entry.TypeName);
if (resolvedType != null && typeof(T).IsAssignableFrom(resolvedType))
{
args.Entry.RemoveProperties(propertiesToRemove);
}
});
}
public static DataServiceClientRequestPipelineConfiguration RemoveProperties<T>(this DataServiceClientRequestPipelineConfiguration requestPipeline, params string[] propertiesToRemove)
{
return requestPipeline.OnEntryStarting((args) =>
{
if (typeof(T).IsAssignableFrom(args.Entity.GetType()))
{
args.Entry.RemoveProperties(propertiesToRemove);
}
});
}
}
Notice that in the method below it is hooking OnEntryStarted. The code in the article hooks OnEntryEnded which worked for me at one point and then broke when I updated to a newer version of ODataClient. OnEntryStarted is the way to go in this method.
public static DataServiceClientRequestPipelineConfiguration RemoveProperties<T>(this DataServiceClientRequestPipelineConfiguration requestPipeline, params string[] propertiesToRemove)
{
return requestPipeline.OnEntryStarting((args) =>
{
if (typeof(T).IsAssignableFrom(args.Entity.GetType()))
{
args.Entry.RemoveProperties(propertiesToRemove);
}
});
}
I also created a partial class for the Container as well and implement the partial method OnContextCreated. This is where you use the extension methods to remove the properties that won't get sent to the server.
partial void OnContextCreated()
{
Configurations.RequestPipeline.RemoveProperties<Customer>(new string[] { "FullName", "VersionDetails" });
Configurations.RequestPipeline.RemoveProperties<SomeOtherType>(new string[] { "IsChecked", "IsReady" });
}
Make sure that your partial classes and the DBContextExtensions class are in the same namespace as our container and everything should just work.
Hope that helps.

Norm.MongoException: Connection timeout trying to get connection from connection pool

I'm using Rob's mvc startesite http://mvcstarter.codeplex.com/ with ASP.Net MVC 2, Ninject2, NoRM (http://github.com/atheken/NoRM) and MongoDB. It works so fast and the developpement is even faster but I'm facing a big problem, I at some points, get connection timeout. I can't figure out what I'm doing wrong.
I already asked a question here : I get this error that I don't understand why, using NoRM and Mongo in my MVC project and here http://groups.google.com/group/norm-mongodb/browse_thread/thread/7882be16f030eb29 but I still in the dark.
Thanks a lot for the help!
EDITED*
Here's my MongoSession object :
public class MongoSession : ISession{
private readonly Mongo _server;
public MongoSession()
{
//this looks for a connection string in your Web.config - you can override this if you want
_server = Mongo.Create("MongoDB");
}
public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class {
return _server.GetCollection<T>().AsQueryable().Where(expression).SingleOrDefault();
}
public IQueryable<T> All<T>() where T : class {
return _server.GetCollection<T>().AsQueryable();
}
public void Save<T>(IEnumerable<T> items) where T : class {
foreach (T item in items) {
Save(item);
}
}
public void Save<T>(T item) where T : class {
var errors = DataAnnotationsValidationRunner.GetErrors(item);
if (errors.Count() > 0)
{
throw new RulesException(errors);
}
_server.Database.GetCollection<T>().Save(item);
}
public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T : class
{
var items = All<T>().Where(expression);
foreach (T item in items)
{
Delete(item);
}
}
public void Delete<T>(T item) where T : class
{
_server.GetCollection<T>().Delete(item);
}
public void Drop<T>() where T : class
{
_server.Database.DropCollection(typeof(T).Name);
}
public void Dispose() {
_server.Dispose();
}
}
And now my MongoRepositoryBase
public abstract class MongoRepositoryBase<T> : ISession<T> where T : MongoObject
{
protected ISession _session;
protected MongoRepositoryBase(ISession session)
{
_session = session;
}
public T Single(ObjectId id)
{
return _session.All<T>().Where(x => x.Id == id).FirstOrDefault();
}
public T Single(Expression<Func<T, bool>> expression)
{
return _session.Single(expression);
}
public IQueryable<T> All()
{
return _session.All<T>();
}
public void Save(IEnumerable<T> items)
{
foreach (T item in items)
{
Save(item);
}
}
public void Save(T item)
{
_session.Save(item);
}
public void Delete(System.Linq.Expressions.Expression<Func<T, bool>> expression)
{
var items = _session.All<T>().Where(expression);
foreach (T item in items)
{
Delete(item);
}
}
public void DeleteAll()
{
var items = _session.All<T>();
foreach (T item in items)
{
Delete(item);
}
}
public void Delete(T item)
{
_session.Delete(item);
}
public void Drop()
{
_session.Drop<T>();
}
public void Dispose()
{
_session.Dispose();
}
}
And an exemple of an other Repository implemantation :
public class PlaceRepository : MongoRepositoryBase<Place>, IPlaceRepository
{
public PlaceRepository(ISession session) : base(session)
{
}
public List<Place> GetByCategory(PlaceCategory category, bool publishedOnly)
{
var query = _session.All<Place>()
.OrderBy(x => x.Name)
.Where(x => x.Category == category);
if (publishedOnly) query = query.Where(x => x.Published);
if (publishedOnly) query = query.Where(x => x.ShowOnMap);
return query.ToList();
}
public Place FindByName(string name)
{
var query = _session.All<Place>()
.Where(x => x.Name.ToLower().Contains(name.ToLower()))
.Where(x => x.Published);
return query.FirstOrDefault();
}
public string[] FindSuggestionsByName(string name)
{
var query = _session.All<Place>()
.OrderBy(x => x.Name)
.Where(x => x.Name.ToLower().StartsWith(name.ToLower()))
.Where(x => x.Published);
var places = query.ToList();
var names = new string[places.Count];
var i = 0;
foreach (var place in places)
{
names[i++] = place.Name;
}
return names;
}
}
Vinny,
I've never used Ninject, so I could be way off with this suggestion. But it seems possible that having a static MongoSession instance might be holding connections open. Have you tried TransientBehavior instead of SingletonBehavior? Or maybe change your code to call Dispose (or use using) after you convert your ShortcutLinks to a List? All
var shortcutLionks = _session.All<ShortcutLinks>().ToList();
_session.Dispose();
A better approach might be to use some sort of repository or DAO where the session details are hidden from the controller. I have a RepositoryBase sample at http://www.codevoyeur.com/Articles/20/A-NoRM-MongoDB-Repository-Base-Class.aspx.
Stuart Harris has a similar, arguably more complete implementation at http://red-badger.com/Blog/post/A-simple-IRepository3cT3e-implementation-for-MongoDB-and-NoRM.aspx
Pooled MongoDB connections are relatively cheap to create, so it's probably best to make sure the data access methods are disposing after your done getting/saving data.
If I add throw new NotImplementedException(); in the Dispose() method of my MongoRepositoryBase class it does not get call so I guess Ninject does not handle this for me, If I had
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
_recipeRepo.Dispose();
base.OnActionExecuted(filterContext);
}
In my controller it does get call. It seems to be fine, thx!

Resources