I'm working on application that uses MongoDb as the database and .Net Core 3.0 as the framework. To fetch data from the database, I have created a DbContext class and using the Aggregation() feature of MongoDb. I'm not able to pass the appropriate projection. Following is the code for DbContext.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
namespace Test.DbContext
{
/// <summary>
/// Standard CRUD Operations with MongoDb
/// </summary>
public class MongoDbContext
{
#region Properties
private readonly IMongoClient _mongoDbClient = null;
private readonly IMongoDatabase _mongoDb = null;
#endregion
#region Constructor
public MongoDbContext(IOptions<MongoSetting> mongoConfigs)
{
_mongoDbClient = new MongoClient(mongoConfigs.Value.ConnectionString);
_mongoDb = _mongoDbClient.GetDatabase(mongoConfigs.Value.DatabaseName);
}
#endregion
#region Grouping
public IList<TProjection> GroupBy<TDocument, TGroupKey, TProjection>
(FilterDefinition<TDocument> filter,
Expression<Func<TDocument, TGroupKey>> selector,
Expression<Func<IGrouping<TGroupKey, TDocument>, TProjection>> projection){
return _mongoDb.GetCollection<TDocument>("collectionName").Aggregate().Match(filter).Group(selector, projection).ToList();
}
#endregion
}
}
To call the function GroupBy(), I have to pass the filter, selector and projection but I'm not able to build the appropriate expression. Following are the data model and calling function:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
namespace Test
{
[BsonIgnoreExtraElements]
public class Employee
{
[BsonId]
[BsonElement("_id")]
[BsonRepresentation(BsonType.ObjectId)]
public ObjectId Id { get; set; }
[BsonElement("type")]
[JsonProperty("type")]
public string Type { get; set; }
[BsonElement("id")]
public string CustomerId { get; set; }
[BsonElement("name")]
public string CustomerName { get; set; }
}
}
I'm calling the dbContext in Customer Repository as:
using System;
using System.Linq.Expressions;
using MongoDB.Driver;
namespace Test.Repositories
{
public class CustomerRepository : ICustomerRepository
{
#region Properties
private readonly IMongoDbContext _dbContext = null;
#endregion
#region Constructor
public CustomerRepository(IMongoDbContext dbContext)
{
_dbContext = dbContext;
}
#endregion
#region Methods
public EmployeeCollection GetSpecificData()
{
Expression<Func<Employee, dynamic>> filter = x => x.Employee.CustomerId == "11";
Expression<Func<Employee, dynamic>> selector = x => new { typeName = x.Employee.Type };
Expression<Func<IGrouping<dynamic, Employee>, dynamic>> projection = x => new
{
Key = x.Key,
count = x.Count(),
avgValue = x.Average(x => Convert.ToInt32(x.Employee.CustomerId))
};
var result = _dbContext.GroupBy<Employee, dynamic, dynamic>(filter, selector, projection);
// Giving exception
// "Value type of serializer is <>f__AnonymousType0`1[[System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral,
//PublicKeyToken=7cec85d7bea7798e]] and does not match member type System.Object. (Parameter 'serializer')"
}
#endregion
}
}
Exception:
"Value type of serializer is <>f__AnonymousType0`1[[System.String,
System.Private.CoreLib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=7cec85d7bea7798e]] and does not match member type
System.Object. (Parameter 'serializer')"
i don't think what you're trying to do is feasible. as an alternative i can suggest to expose the .Aggregate() from the dbContext and query from the repo like below.
Employee Class
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace Test
{
[BsonIgnoreExtraElements]
public class Employee
{
[BsonId]
[BsonElement("_id")]
[BsonRepresentation(BsonType.ObjectId)]
public ObjectId Id { get; set; }
[BsonElement("type")]
public string Type { get; set; }
[BsonElement("id")]
[BsonRepresentation(BsonType.String)] // to avoid manual conversion
public int CustomerId { get; set; }
[BsonElement("name")]
public string CustomerName { get; set; }
}
}
DB Context
using MongoDB.Driver;
using System.Linq;
namespace Test.DbContext
{
public class MongoDbContext
{
private readonly IMongoClient _mongoDbClient = null;
private readonly IMongoDatabase _mongoDb = null;
public MongoDbContext()
{
_mongoDbClient = new MongoClient("mongodb://localhost");
_mongoDb = _mongoDbClient.GetDatabase("test");
}
public IAggregateFluent<TDocument> Aggregate<TDocument>() =>
_mongoDb.GetCollection<TDocument>(nameof(TDocument)).Aggregate();
}
}
Repository
using MongoDB.Driver;
using System.Collections.Generic;
using System.Linq;
using Test.DbContext;
namespace Test.Repositories
{
public class CustomerRepository
{
private static readonly MongoDbContext _dbContext = new MongoDbContext();
public List<dynamic> GetSpecificData()
{
var result = _dbContext.Aggregate<Employee>()
.Match(e => e.CustomerId == 11)
.Group(e => e.Type, g => new { Key = g.Key, Count = g.Count(), Average = g.Average(e => e.CustomerId) })
.ToList();
return result.Cast<dynamic>().ToList();
}
}
}
i'd also recommend you to not cast anonymous type to dynamic. so, create and use another class (for group result) to retain type-safety.
i can also recommend for you to have a look at a library i've written which eradicates the need to write your own dbContext.
then have a look at this starter template project to see it in use.
Related
I have an application I haven't touched for a while and it is giving me some grief.
When I call the index method of the controller I get the following error:
I am not sure what I am doing wrong but it would seem that AutoMapper is having trouble mapping a collection of Shift objects to a ShiftViewModel.
I have included some snippets below.
Thoughts?
My controller:
using AutoMapper;
using My.DataAccess;
using My.Entity.DatabaseEntities;
using My.Entity.ViewModels;
using My.Service;
using System.Net;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace My.Controllers
{
public class ShiftController : Controller
{
//initialize service object
readonly IShiftService _shiftService;
private readonly IMapper _mapper;
public ShiftController(IShiftService shiftService, IMapper mapper)
{
_shiftService = shiftService;
_mapper = mapper;
}
readonly ApplicationDataManager db = new ApplicationDataManager();
// GET: /Shifts/
public ActionResult Index()
{
var shifts = _shiftService.GetAll();
if (shifts == null)
{
return HttpNotFound();
}
var model = _mapper.Map<ShiftViewModel>(shifts);
return View(model);
}
}
}
Shift database entity:
using System;
using System.ComponentModel.DataAnnotations;
namespace My.Entity.DatabaseEntities
{
public class Shift : AuditableEntity<long>
{
[Required, StringLength(6)]
[Editable(true)]
public string Code { get; set; }
public string Detail { get; set; }
public DateTime Start { get; set; }
public long Duration { get; set; }
}
}
ShiftViewModel class:
using System;
using System.ComponentModel.DataAnnotations;
namespace My.Entity.ViewModels
{
public class ShiftViewModel
{
public int Id { get; set; }
public string Code { get; set; }
public string Detail { get; set; }
public DateTime Start { get; set; }
[Display(Name = "Duration of shift")]
[DisplayFormat(DataFormatString = "{0:HH:mm}", ApplyFormatInEditMode = true)]
[DataType(DataType.Duration)]
public string DurationTime
{
get
{
var ts = new TimeSpan(Duration);
var h = ts.Hours == 1 ? "hour" : "hours";
var m = ts.Minutes == 1 ? "min" : "mins";
return string.Format("{0} {1} {2} {3}", ts.Hours, h, ts.Minutes, m);
}
}
public long Duration { get; set; }
}
}
Global.asax:
using Autofac;
using Autofac.Integration.Mvc;
using My.DataAccess.Modules;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace My.App
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//Autofac Configuration
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly).PropertiesAutowired();
builder.RegisterModule(new RepositoryModule());
builder.RegisterModule(new ServiceModule());
builder.RegisterModule(new EFModule());
//Register AutoMapper here using AutoFacModule class (Both methods works)
//builder.RegisterModule(new AutoMapperModule());
builder.RegisterModule<AutoFacModule>();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
}
AutoFacModule:
using Autofac;
using AutoFacAndAutoMapperMVC.Infrastructure;
using AutoMapper;
public class AutoFacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(context => new MapperConfiguration(cfg =>
{
//Register Mapper Profile
cfg.AddProfile<AutoMapperProfile>();
}
)).AsSelf().InstancePerRequest();
builder.Register(c =>
{
//This resolves a new context that can be used later.
var context = c.Resolve<IComponentContext>();
var config = context.Resolve<MapperConfiguration>();
return config.CreateMapper(context.Resolve);
})
.As<IMapper>()
.InstancePerLifetimeScope();
}
}
AutoMapperProfile:
using Roster.Entity.DatabaseEntities;
using Roster.Entity.ViewModels;
using AutoMapper;
namespace AutoFacAndAutoMapperMVC.Infrastructure
{
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Shift, ShiftViewModel>();
CreateMap<ShiftViewModel, Shift>();
}
}
}
Trekco, It is called by a generic method of IEnumerable
public virtual IEnumerable<T> GetAll()
{
return _repository.GetAll();
}
it returns a Shift object
In you Index method, the code -
var shifts = _shiftService.GetAll();
is definitely not returning a single Shift object. I guess, its returning a list/collection of Shift object. If so, then with the code -
var model = _mapper.Map<ShiftViewModel>(shifts);
you are trying to map a list of Shift object to a single ShiftViewModel object which is causing the issue.
Change the mapping code to -
var model = _mapper.Map<List<ShiftViewModel>>(shifts);
So this is how I solved the problem. Thanks everyone for the help.
Solution
So it seems, as Akos Nagy, points out in his blog post the problem is that AutoMapper and Autofac don't play very well together. This wasn't helped by the fact that my code was not very good to begin with.
The AutoMapper docs had a page on Dependency Injection found here. There were no real examples for AutoFac however it did point me towards a Nuget package named AutoMapper.Contrib.Autofac.DependencyInjection 5.2.0. There is a GitHub project here.
So I installed that package with the Package Manager Console.
Install-Package AutoMapper.Contrib.Autofac.DependencyInjection -Version 5.2.0
I then simplified my Shift domain object class.
using System;
using System.ComponentModel.DataAnnotations;
namespace Roster.Entity.DatabaseEntities
{
public class Shift : AuditableEntity<long>
{
#region Public Properties
[Required, StringLength(6)]
[Editable(true)]
[Display(Name = "Code")]
public string Code { get; set; }
public string Detail { get; set; }
public DateTime Start { get; set; }
public long Duration { get; set; }
#endregion Public Properties
}
}
Next I reworked my ViewModel, again just to make things a bit cleaner and adding a little business logic functionality.
using System;
using System.ComponentModel.DataAnnotations;
namespace Roster.Entity.ViewModels
{
public class ShiftViewModel : AuditableEntity<long>
{
[Required, StringLength(6)]
[Editable(true)]
[Display(Name = "Code")]
public string Code { get; set; }
public string Detail { get; set; }
[DisplayFormat(DataFormatString = "{0:HH:mm }", ApplyFormatInEditMode = true)]
public DateTime Start { get; set; }
public long Duration { get; set; }
[Display(Name = "Text Duration time of shift")]
[DisplayFormat(DataFormatString = "{0:hh:mm}", ApplyFormatInEditMode = true)]
[DataType(DataType.Duration)]
public string DurationTime
{
get
{
TimeSpan ts = new TimeSpan(Duration);
var h = ts.Hours == 1 ? "hour" : "hours";
var m = ts.Minutes == 1 ? "min" : "mins";
return string.Format("{0} {1} {2} {3}", ts.Hours, h, ts.Minutes, m);
}
}
[Display(Name = "Shift Duration")]
[DisplayFormat(DataFormatString = "{0:hh\\:mm}", ApplyFormatInEditMode = true)]
[DataType(DataType.Duration)]
public string ShiftDuration
{
get
{
return TimeSpan.FromTicks(Duration).ToString();
}
set
{
TimeSpan interval = TimeSpan.FromTicks(Duration);
Duration = interval.Ticks;
}
}
}
}
Now on to mapping the domain object to my ViewModel.
First I needed to create an AutoMapper profile to create the map. I saved this in the APP_Start folder for no reason than I thought it a good place.
using AutoMapper;
using Roster.Entity.DatabaseEntities;
using Roster.Entity.ViewModels;
namespace AutoFacAndAutoMapperMVC.Infrastructure
{
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Shift, ShiftViewModel>()
.ReverseMap();
}
}
}
Next I needed to change Global.asax.cs
I registered AutoMapper by adding
builder.RegisterAutoMapper(typeof(MvcApplication).Assembly);
I then added the following lines to resolve the mapper service and control the lifetime scope. I am not sure what this actually does but it is recommended in the AutoFac docs here
using(var scope = container.BeginLifetimeScope())
{
var service = scope.Resolve<IMapper>();
}
Finally I altered the Shift Controller to use AutoMapper.
using AutoMapper;
using Roster.DataAccess;
using Roster.Entity.ViewModels;
using Roster.Service;
using System.Collections;
using System.Collections.Generic;
using System.Web.Mvc;
namespace Roster.Controllers
{
public class ShiftController : Controller
{
//initialize service object
private readonly IShiftService _shiftService;
private readonly IMapper _mapper;
public ShiftController(IShiftService shiftService, IMapper mapper)
{
_shiftService = shiftService;
_mapper = mapper;
}
readonly ApplicationDataManager db = new ApplicationDataManager();
// GET: /Shifts/
public ActionResult Index()
{
IEnumerable shifts = _shiftService.GetAll();
var model = _mapper.Map<IEnumerable<ShiftViewModel>>(shifts);
if (model == null)
{
return HttpNotFound();
}
return View(model);
}
}
}
Importantly and a real beginners error on my behalf is that because I had a collection of shifts to map I had to have a collection of viewmodels. Thanks atiyar. I resolved this by mapping like this. So stupid on my behalf.
var model = _mapper.Map<IEnumerable<ShiftViewModel>>(shifts);
So sorry about the long answer but I thought I would wrap up the question with how I resolved my problem. It was a great learning exercise for someone who is not a professional programmer. Thanks everyone.
The overall goal is to be able to perform a lookup of a meal based on the userid, and mealid. If that item is found, I want to be able to delete only the entire element that matches both the userid and mealid.
Currently, After sending the request object via postman in a post request, I have tried to write a variation of queries using the C# to create a builder object, and then filter to find the specific array object, and finally delete the array object if there is a match on both userid and mealid. Initially, I was getting the issue where the entire element was not being deleted, but only the interal array element that is nested inside of the element was being (not deleted, but set back to null values). However, now the issue is that the entire array element is not being deleted at all and i'm getting the following error.
BsonArraySerializer Error from Visual Studio
Can someone please help me to resolve this issue?
Here is a sample object that I'm sending via postman that I'm trying to delete:
Sample Postman POST request with Body data
Here is an example image of data that I'm trying to delete:
Sample Image of Json Array Elemet I'm trying to delete
You will need MongoDb Compass or Atlas, .NET Core 3.1, MongoDB C# Driver 2.0, Postman, and .NET Core 3.1 WebApi Project/Solution in order to help solve this issue.
Below, is the code that is needed to replicate the issue:
Startup.cs
Add this line of code to the Configuration method of this file
services.AddScoped<IMealsRepository, MealsRepository>();
Add this line to the ConfigureServices Method
services.AddSingleton(sp =>
sp.GetRequiredService<IOptions<DatabaseSettings>>().Value);
appSettings.json
Add these lines of code to this file
"DatabaseSettings": {
"ConnectionString": "your connection string to MongoDb"
}
Database Settings.cs
public class DatabaseSettings
{
public string ConnectionString { get; set; }
}
MealPlanModel.cs:
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
public class MealPlanModel
{
#region MealPlanModel fields
[BsonElement("userid")]
public int? UserId { get; set; }
[BsonElement("mealid")]
public int? MealId { get; set; }
[BsonElement("mealname")]
public string MealName { get; set; }
[BsonElement("mealimage")]
public string MealImage { get; set; }
[BsonElement("foods")]
public FoodModel[] Foods { get; set; }
[BsonElement("totalcalories")]
public double TotalCalories { get; set; }
[BsonElement("totalprotein")]
public double TotalProtein { get; set; }
[BsonElement("totalcarbs")]
public double TotalCarbs { get; set; }
[BsonElement("totalfat")]
public double TotalFat { get; set; }
[BsonElement("totalsugar")]
public double TotalSugar { get; set; }
[BsonElement("totalfiber")]
public double TotalFiber { get; set; }
#endregion
#region MealPlanModel ctor
public MealPlanModel()
{
}
public MealPlanModel(int userid, int mealid)
{
UserId = userid;
MealId = mealid;
}
}
MealPlanDto.cs
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System.Collections.Generic;
public class MealPlanDto
{
[BsonId]
public ObjectId Id { get; set; }
[BsonElement("meals")]
public List<MealPlanModel> MealPlans { get; set; }
}
**MealsController.cs:**
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
[Route("api/[controller]")]
[ApiController]
public class MealsController : ControllerBase
{
private readonly IMealsRepository _repo;
private readonly IMapper _mapper;
public MealsController(IMealsRepository repo, IMapper mapper)
{
_repo = repo;
_mapper = mapper;
}
[HttpGet, Route("CheckConnection")]
public async Task<IActionResult> CheckConnection()
{
var result = await _repo.CheckConnection();
if (result == null || result.Count <= 0)
return BadRequest("Failed to connect to database.");
return Ok("Database connection was successful");
}
[HttpPost("deletecustommeal")]
public async Task<IActionResult> DeleteCustomMealPlan(int id)
{
var requestBody = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
var mealPlanToDelete = JsonConvert.DeserializeObject<MealPlanModel>(requestBody);
MealPlanDto deleteMealPlan = new MealPlanDto();
deleteMealPlan.MealPlans = new List<MealPlanModel>();
deleteMealPlan.MealPlans.Add(mealPlanToDelete);
var result = await _repo.DeleteCustomMealPlanById(deleteMealPlan);
if (!result)
return BadRequest("Failed to delete meal");
return Ok("Successfully deleted meal plan");
}
}
MealsRepository.cs
using Microsoft.Extensions.Configuration;
using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class MealsRepository : IMealsRepository
{
private readonly MongoClient _client;
private readonly IMongoDatabase _database;
private readonly IMongoCollection<MealPlanDto> _userMealsCollection;
public MealsRepository(IConfiguration configuration)
{
_client = new
MongoClient(configuration.GetSection("DatabaseSettings").GetSection("ConnectionString").Value);
_database = _client.GetDatabase("MealsDb");
_userMealsCollection = _database.GetCollection<MealPlanDto>("meal");
}
public async Task<List<BsonDocument>> CheckConnection()
{
List<BsonDocument> list = await _database.ListCollections().ToListAsync();
var populatedList = (list != null && list.Count > 0) ? list : null;
return populatedList;
}
public async Task<bool> DeleteCustomMealPlanById(MealPlanDto mealPlanToDelete)
{
var builder = Builders<MealPlanDto>.Filter;
var filter = builder.Eq(x => x.MealPlans[0].UserId, mealPlanToDelete.MealPlans[0].UserId);
var update = Builders<MealPlanDto>.Update.PullFilter(
p => (IEnumerable<MealPlanModel>)p.MealPlans[0],
f => f.MealId.Value == mealPlanToDelete.MealPlans[0].MealId);
try
{
await _userMealsCollection.UpdateOneAsync(filter, update);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"Failed to delete meal plan. {ex} occured.");
return false;
}
}
}
Thanks for all that attempted to find an answer to my question above, but I actually discovered a simple solution
I simply replaced the above method in the Repository.cs file with the following and it works like a charm
public bool DeleteCustomMealPlanForUserById(MealPlanModel mealPlanToDelete)
{
var result = _customUserMealsCollection.DeleteOne(p => p.MealPlans[0].UserId == mealPlanToDelete.UserId
&& p.MealPlans[0].MealId == mealPlanToDelete.MealId);
return result.DeletedCount != 0;
}
I'm rewriting a current working POS system that was created in C# using the .NET Framework. When trying to build the project the compiler returns error CS0311 saying that the type of the parameter given can't be used.
I've researched the used classes and everything it needs according to the .NET documentation is defined in the class so I have no clue why it doesn't work.
The code used in EfDbContext.cs:
using NLog;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Data.Entity.Validation;
using System.Linq.Expressions;
using Dal.Migrations;
using Dal.Model;
namespace Dal
{
public class EfDbContext : DbContext
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public EfDbContext()
: base(Settings.ConnectionString ?? "")
{
Database.SetInitializer<EfDbContext>((IDatabaseInitializer<EfDbContext>) new MigrateDatabaseToLatestVersion<EfDbContext, Configuration>());
EfDbContext.logger.Debug("Database entity context initialized");
}
public DbSet<Member> Members { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<TaxCategory> TaxCategories { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<TicketLine> TicketLines { get; set; }
public DbSet<MemberCard> MemberCards { get; set; }
public DbSet<CheckoutSheet> CheckoutSheets { get; set; }
public DbSet<Transaction> Transactions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Entity<Ticket>().HasMany<TicketLine>((Expression<Func<Ticket, ICollection<TicketLine>>>)(p => p.TicketLines)).WithRequired().WillCascadeOnDelete(true);
modelBuilder.Entity<Ticket>().HasMany<Transaction>((Expression<Func<Ticket, ICollection<Transaction>>>)(p => p.Transactions)).WithOptional((Expression<Func<Transaction, Ticket>>)(m => m.Ticket)).WillCascadeOnDelete(true);
modelBuilder.Entity<Member>().HasMany<MemberCard>((Expression<Func<Member, ICollection<MemberCard>>>)(p => p.MemberCards)).WithRequired((Expression<Func<MemberCard, Member>>)(m => m.Member));
modelBuilder.Entity<CheckoutSheet>().HasMany<Ticket>((Expression<Func<CheckoutSheet, ICollection<Ticket>>>)(p => p.Tickets)).WithOptional((Expression<Func<Ticket, CheckoutSheet>>)(m => m.CheckoutSheet)).WillCascadeOnDelete(true);
}
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbEntityValidationException ex)
{
throw new FormattedDbEntityValidationException(ex);
}
}
~EfDbContext()
{
EfDbContext.logger.Debug("Database entity context destroyed");
}
}
}
Code used in Configuration.cs:
using NLog;
using System.Data.Entity;
using System.Data.Entity.Migrations;
namespace Dal.Migrations
{
internal sealed class Configuration : DbMigrationsConfiguration<DbContext>
{
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
public Configuration()
{
this.AutomaticMigrationsEnabled = true;
this.AutomaticMigrationDataLossAllowed = true;
this.ContextKey = "POS.Dal.EfDbContext";
}
protected override void Seed(DbContext context)
{
Configuration.logger.Info("Database migration completed");
}
}
}
[CS0311 error returned by compiler][1]
Error CS0311
The type 'Dal.Migrations.Configuration' cannot be used as type parameter 'TMigrationsConfiguration' in the generic type or method 'MigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration>'. There is no implicit reference conversion from 'Dal.Migrations.Configuration' to 'System.Data.Entity.Migrations.DbMigrationsConfiguration<Dal.EfDbContext>'.
Dal 2
D:\lagae\Documents\Point Of Sale\POS\Dal
D:\lagae\Documents\Point Of Sale\POS\Dal\EfDbContext.cs
Line 20
Column 134
Does somebody see what I did wrong?
Kind Regards,
Jerlag_01
I just started off with programming in C#. I'm using Visual Studio to make a Mongo database and followed a tutorial to get the following:
using System;
using System.Collections.Generic;
using System.Text;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace NetCoreWeb.Repositories
{
public class BreakoutStrategyConfig
{
[BsonElement("id")]
public ObjectId id { get; set; }
[BsonElement("stock")]
public string stock { get; set; }
[BsonElement("trade_limit")]
public int trade_limit { get; set; }
[BsonElement("interval")]
public double interval { get; set; }
[BsonElement("exit_pl")]
public double exit_pl { get; set; }
}
}
This is the implementation:
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
namespace NetCoreWeb.Repositories
{
public class BreakoutStrategyConfigImpl : IBreakoutStrategyConfig
{
private readonly IMongoDatabase _db;
public BreakoutStrategyConfigImpl()
{
MongoClient client = new MongoClient("mongodb://mymongo:27017");
_db = client.GetDatabase("BreakoutStrategyConfig");
}
public ICollection<BreakoutStrategyConfig> GetAllBreakoutStrategyConfigs()
{
List<BreakoutStrategyConfig> configs = new List<BreakoutStrategyConfig>();
var cursor = _db.GetCollection<BreakoutStrategyConfig>("BreakoutStrategyConfigs").Find(new BsonDocument()).ToCursor();
foreach (var document in cursor.ToEnumerable())
{
configs.Add(document);
}
return configs;
}
public BreakoutStrategyConfig GetBreakoutStrategyConfigById(ObjectId id)
{
return _db.GetCollection<BreakoutStrategyConfig>("BreakoutStrategyConfigs").Find(config => config.id == id).FirstOrDefault();
}
public void AddBreakoutStrategyConfig(BreakoutStrategyConfig config)
{
_db.GetCollection<BreakoutStrategyConfig>("BreakoutStrategyConfigs").InsertOne(config);
}
public void DeleteBreakoutStrategyConfig(ObjectId id)
{
_db.GetCollection<BreakoutStrategyConfig>("BreakoutStrategyConfigs").DeleteOne(config => config.id == id);
}
public void UpdateBreakoutStrategyConfig(BreakoutStrategyConfig config)
{
_db.GetCollection<BreakoutStrategyConfig>("BreakoutStrategyConfigs").ReplaceOneAsync(n => n.id.Equals(config.id)
, config
, new UpdateOptions { IsUpsert = true });
}
}
}
My problem is pretty simple: I made the classes and tables but I'm not sure how to test this.
How can I see a list of collections a documents in the database?
Do I write a script in Program.cs (automatically created)?
Essentially, I just need to see if this works.
Did you mean that collection and database is created by your code or not?
if yes, you don't need to manually run the script to create db and collection.
If DB / collection is not exist then mongodb will automatically creates it.
I am creating an application in which I use Entity Framework. I have 2 classes with one-to-many relation .I decided to use a design pattern Repository as far as I know it is a good practice.
My Interface:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace DataAccess.Repository
{
public interface IRepository<T>
{
void Insert(T entity);
void Delete(T entity);
IQueryable<T> SearchFor(Expression<Func<T, bool>> predicate);
IEnumerable<T> GetAll();
T GetById(int id);
}
}
My class
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using DataAccess.Repository;
namespace DataAccess
{
public class Repository<T> : IRepository<T> where T : class
{
protected DbSet<T> DbSet;
public Repository(DbContext datacontext)
{
//DbContext.Set Method (Type)
//Returns a non-generic DbSet instance for access to entities of the given type in the context and the underlying store.
DbSet = datacontext.Set<T>();
}
public void Insert(T entity)
{
DbSet.Add(entity);
}
public void Delete(T entity)
{
DbSet.Remove(entity);
}
public IQueryable<T> SearchFor(Expression<Func<T, bool>> predicate)
{
return DbSet.Where(predicate);
}
public IEnumerable<T> GetAll()
{
return DbSet;
}
public T GetById(int id)
{
return DbSet.Find(id);
}
}
}
And this is my two model classes
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
public class Book
{
public int BookId { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string Ganre { get; set; }
public int Size { get; set; }
public string Path { get; set; }
}
}
and
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Model
{
public class Category{
public Category()
{
Books = new List<Book>();
}
public int CategoryId { get; set; }
public string CategoryName { get; set; }
virtual public ICollection<Book> Books { get; set; }
}
}
But my question is, how to add the book to a category?
This is an example of my implementation but book is not added to the category. But when I want to get all the books or all categories everything works fine.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
using Model;
using DataAccess;
namespace TestDB
{
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(
new DropCreateDatabaseIfModelChanges<BookShelfContext>());
using (var db = new BookShelfContext())
{
var book = new Book
{
Author = "Author Name",
Ganre = "Ganre",
Name = "Book Name",
Path = #"Path",
Size = 10
};
var category = new Category
{
CategoryName = "Interesting"
};
var bookrepository = new Repository<Book>(db);
var categoryrepository = new Repository<Category>(db);
IEnumerable<Book> books = bookrepository.GetAll();
IEnumerable<Category> categories = categoryrepository.GetAll();
//get all books for example
foreach (var b in books)
{
Console.WriteLine(b.Name);
}
}
Console.ReadKey();
}
}
}
Thank you very much for your help. Have a nice day and less bugs)
Add the context to your Repository so that you can implement the SaveChanges method:
protected readonly DbContext context;
public Repository(DbContext datacontext)
{
DbSet = datacontext.Set<T>();
context = datacontext;
}
public void SaveChanges()
{
context.SaveChanges();
}
Then in order to add a book to an existing BookCategory simply add the book to the Category's collection and save the category:
var categoryrepository = new Repository<Category>(db);
var myCategory = categoryrepository.GetById(1);
myCategory.Books.Add(book);
categoryrepository.SaveChanges();
Remember to call SaveChanges in order to persist the data in the database. EF is smart enough to notice that you added a child to the category and it will mark it as Added. On saving changes it will insert it to the database together with the foreign keys that it needs.
I think you need to add a category property to the book object
public virtual Category BookCategory { get; set; }
so that the one to many relationship is implemented