Repository pattern and one to many relationship - c#

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

Related

InvalidOperationException: Unable to resolve service

first sorry if my english isn't good enough XD. Second, I'm programing on ASP.NET Core 2.0. And when i launch my webapp i get the next error when i try to go to the "Clases" web page of my App.
InvalidOperationException: Unable to resolve service for type 'EduCloud.Data.IClassInfo' while attempting to activate 'EduCloud.Controllers.UnitsController'.
Before all, sorry if it is too much code. The code for the Model involve is
using System;
using System.Collections.Generic;
using System.Text;
namespace EduCloud.Data.Models
{
public class ClassInfo
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string ClassVideoUrl { get; set; }
public string ClassHomeworkUrl { get; set; }
public string TestUrl { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual Forum Forum { get; set; }
}
}
This Model has 1 level of abstraction with the class
using EduCloud.Data.Models;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace EduCloud.Data
{
public interface IClassInfo
{
ClassInfo GetById(int id);
IEnumerable<ClassInfo> GetAll();
IEnumerable<ClassInfo> GetFilteredClasses(Forum forum, string searchQuery);
IEnumerable<ClassInfo> GetFilteredClasses(string searchQuery);
IEnumerable<ClassInfo> GetClassesByForum(int id);
Task Add(ClassInfo classInfo);
Task Delete(int id);
Task EditClassContent(int id, string newContent);
}
}
Next, the methods of the this IClass are in the next class
using EduCloud.Data;
using EduCloud.Data.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EduCloud.Service
{
public class ApplicationClassService : IUnit
{
private readonly ApplicationDbContext _context;
public ApplicationClassService(ApplicationDbContext context)
{
_context = context;
}
public async Task Create(ClassInfo classInfo)
{
_context.Add(classInfo);
await _context.SaveChangesAsync();
}
public Task Delete(int classId)
{
throw new NotImplementedException();
}
public IEnumerable<Forum> GetAll()
{
return _context.Forums
.Include(forum => forum.Classes);
}
public Forum GetById(int id)
{
var forum = _context.Forums.Where(f => f.Id == id)
.Include(f => f.Classes ).ThenInclude(p => p.User)
//.Include(f => f.Classes).ThenInclude(p => p.Replies).ThenInclude(r => r.User)
.FirstOrDefault();
return forum;
}
public Task UpdateForumTitle(int classId, string newTitle)
{
throw new NotImplementedException();
}
}
}
Finally the Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EduCloud.Data;
using EduCloud.Data.Models;
using EduCloud.Models.Classes;
using EduCloud.Models.Units;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace EduCloud.Controllers
{
public class UnitsController : Controller
{
private readonly IUnit _unitService;
private readonly IForum _forumService;
private readonly IClassInfo _classService;
public UnitsController(IUnit unitService, IForum forumService, IClassInfo classService)
{
_unitService = unitService;
_forumService = forumService;
_classService = classService;
}
public IActionResult Index()
{
var units = _unitService.GetAll()
.Select(forum => new UnitsListingModel
{
Id = forum.Id,
Name = forum.Title,
});
var model = new UnitsIndexModel
{
UnitList = units
};
return View(model);
}
}
}
And the code of the view that i try to run when i go to the "Clases" link
#model EduCloud.Models.Units.UnitsIndexModel
<h1>Unidades de Aprendizaje</h1>
<table class="table table-hover" id="forumIndexTable">
<tbody>
#foreach (var forum in Model.UnitList)
{
<tr>
<td>
<a asp-controller="Units" asp-action="ClassesList" asp-route-id="#forum.Id">
#forum.Name
</a>
</td>
</tr>
}
</tbody>
</table>

CRUD WEB API with entity framework

I started develop my project - web application with Database. I used WEB API with entity framework
I need CRUD operations realize in my project.
Read - work fine
But I don't know how realize Create, Update, Delete; I don't have enough experience and be glad yours advice.
I tried realize good architecture of my application - using repository pattern and fabric pattern. If you have advice in architecture of my project , I'll be grateful you.
I don't know how realize it in value controller and repository, could you help please?
Attach my code:
Repository
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WebAPI
{
public class CustomerRepository
{
public IQueryable<Customer> GetAllCustomers()
{
DevelopersEntities dev = new DevelopersEntities();
return dev.Customers;
}
public IQueryable<Customer> GetAllCustomers(int id)
{
DevelopersEntities dev = new DevelopersEntities();
return dev.Customers.Where(c=>c.Id==id).Select(e=>e);
}
public IQueryable<Customer> DeleteCustomer(int id)
{
DevelopersEntities dev = new DevelopersEntities();
return dev.Customers.Remove(id);
}
public IQueryable<Customer> CreateCustomer()
{
DevelopersEntities dev = new DevelopersEntities();
}
public IQueryable<Customer> UpdateCustomer(int id)
{
DevelopersEntities dev = new DevelopersEntities();
}
}
}
Customer model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WebAPI;
namespace DevelopersWeb.Models
{
public class CustomerModel
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
public IEnumerable<HardwareModel> Hardware { get; set; }
public IEnumerable<SoftwareModel> Software { get; set; }
}
}
Harware Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DevelopersWeb.Models
{
public class HardwareModel
{
public int HardwareId { get; set; }
public string HardwareName { get; set; }
}
}
Software Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DevelopersWeb.Models
{
public class SoftwareModel
{
public int SoftwareId { get; set; }
public string SoftwareName { get; set; }
}
}
Model Factory
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using WebAPI;
namespace DevelopersWeb.Models
{
public class ModelFactory
{
public CustomerModel Create(Customer customer)
{
return new CustomerModel()
{
CustomerId = customer.Id,
CustomerName = customer.Name,
Hardware = customer.HardWares.Select(h=>Create(h)),
Software = customer.Softwares.Select(c=>Create(c))
};
}
public HardwareModel Create(HardWare hardware)
{
return new HardwareModel()
{
HardwareId = hardware.HardWareId,
HardwareName = hardware.HardWareName,
};
}
public SoftwareModel Create(Software software)
{
return new SoftwareModel()
{
SoftwareId = software.SoftwareId,
SoftwareName = software.SoftwareName
};
}
}
}
Value Controller
using DevelopersWeb.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using WebAPI;
namespace DevelopersWeb.Controllers
{
public class ValuesController : ApiController
{
ModelFactory _modelFactory;
public ValuesController()
{
_modelFactory = new ModelFactory();
}
// GET api/values
public IEnumerable<CustomerModel> Get()
{
CustomerRepository cr = new CustomerRepository();
return cr.GetAllCustomers().ToList().Select(c=> _modelFactory.Create(c));
}
// GET api/values/5
public string Get(int id)
{
return "xxx";
}
// POST api/values
public void Post([FromBody]string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
}
Here is how you should save customer object:
public void CreateCustomer(Customer customer)
{
DevelopersEntities dev = new DevelopersEntities();
dev.Customers.Add(customer)
dev.SaveChanges();
}
Here is you api action:
// POST api/values
public void Post([FromBody]CustomerModel customerModel)
{
//Here you should transform CustomerModel object to customerEntity
CustomerRepository cr = new CustomerRepository();
cr.CreateCusomer(customer);
return Ok();
}
You should think about using Dependency Injection pattern here as well.

MongoDb C# Driver with Group and Project using Aggregate() query giving exception

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.

Debugging MongoDB in C#

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.

How to delete item from SQLite PCL database on Xamarin.Forms?

I am using one of the Xamarin.Forms tutorials on SQLite database deployment to learn how to add, get and delete items from a mobile database. At the moment, I am not quite sure how to retrieve a the Id attribute from a database item in my table to pass it through the deleteData method. Basically, after I add in a certain amount of items, I would like to delete some of the data. I have implemented a button in my XAML binding pages but I need to know how to retrieve the Id attribute of certain items and pass them into the deleteData method I have created. Can anyone help me with this?
Here are the codes:
StudentDB.cs (database class)
using System;
using System.Linq;
using SQLite.Net;
using Xamarin.Forms;
using System.Collections.Generic;
namespace XamarinSqliteSample
{
public class StudentDB
{
private SQLiteConnection _sqlconnection;
public StudentDB ()
{
_sqlconnection = DependencyService.Get<ISQLite> ().GetConnection ();
_sqlconnection.CreateTable<Student> ();
}
public IEnumerable<Student> GetStudents()
{
return (from t in _sqlconnection.Table<Student> ()
select t).ToList ();
}
public Student GetStudent (int id)
{
return _sqlconnection.Table<Student> ().FirstOrDefault (t => t.Id == id);
}
public void DeleteStudent(int id)
{
_sqlconnection.Delete<Student>(id);
}
public void AddStudent(Student student)
{
_sqlconnection.Insert(student);
}
}
}
Student.cs (item attributes for database table)
using System;
using SQLite.Net.Attributes;
namespace XamarinSqliteSample
{
public class Student
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public Student ()
{
}
}
}
Register.xaml.cs (C# page to create methods to that manipulate data)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace XamarinSqliteSample
{
public partial class Register : ContentPage
{
public StudentDB _studentdb;
public Student student;
public Register()
{
InitializeComponent();
}
public void adddata(object s, EventArgs args)
{
student = new Student();
_studentdb = new StudentDB();
student.Name = name.Text;
student.Address = address.Text;
student.Phone = phone.Text;
student.Id++;
_studentdb.AddStudent(student);
}
public void Showdata(object sender, EventArgs args)
{
Navigation.PushModalAsync(new StudentList());
}
}
}
StudentList.xaml.cs (C# page to populate list view with database items)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace XamarinSqliteSample
{
public partial class StudentList : ContentPage
{
public StudentDB _database;
public Student student;
public StudentList()
{
InitializeComponent();
_database = new StudentDB();
var students = _database.GetStudents();
StudentListView.ItemsSource = students;
}
public void deleteData(object s, EventArgs args)
{
_database = new StudentDB ();
_database.DeleteStudent (//Id attribute is passed in here);
}
}
It depends on how you trigger deleteData, but you'll probably want to use StudentListView.ItemSelected, which should be one of your Student objects.

Categories

Resources