Map database first model to domain entity with OnModelCreatingPartial - c#

I'm trying to follow the 'Clean Architecture' pattern with a database first approach.
So I have in the 'Domain' project a entity called 'Contract'.
public class Contract
{
public string ContractNumber { get; set; } = string.Empty;
}
And in the 'Application' project an interface with this type.
public interface IContractRepository : IAsyncRepository<Contract>
{
}
After 'Scaffolding' the existing database in the 'Infrastructure' project.
There is this in the 'MyDbContext' class.
public virtual DbSet<DbContractName> DbContractNames { get; set; }
Ofcours the 'Contract' repository has the type 'Contract'.
public class ContractRepository : BaseRepository<Contract>, IContractRepository
{
public ContractRepository(MyDbContext dbContext)
: base(dbContext)
{
}
}
Is it possible and how (example) can I overwrite or map the 'DbSet' 'DbContractNames' to 'Contracts'?
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
Error message :
Cannot create a DbSet for 'Contract' because this type is not included in the model for the context.

Related

EF Core - Reuse the same join table entity for several relationships

I want to reuse the same many-to-many relationship table (FileInEntity) for several other objects (Course, Lecture, Game), since they all can have files. Since we have to manually create the many-to-many relationships by creating a join entity, I want to reuse the join entity for the objects (Course, Lecture, Game).
If we look at the table structure, I would like to have the following:
Course: Id, ...
Lecture: Id, ...
Game: Id, ...
FileInEntity: EntityId (this can be either Course.Id, Lecture.Id or Game.Id), FileId
File: Id, ...
(File is base class type with two derived types: Image and Audio)
When I try this approach in .NET Core, I receive the following error message:
Entity type 'FileInEntities' is in shadow-state. A valid model requires
all entity types to have corresponding CLR type.
Is this even possible?
This is my setup:
ModelBase.cs
public class ModelBase
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
}
Course.cs
[Table("Courses")]
public class Course : ModelBase
{
private ICollection<FileInEntity> IconsInCourse { get; set; } = new List<FileInEntity>();
[NotMapped]
public File Image => IconsInCourse.Select(e => e.File).FirstOrDefault();
}
Lecture.cs
// Same as Course.cs
Game.cs
// Same as Course.cs
FileInEntity.cs
[Table("FilesInEntities")]
public class FileInEntity
{
public Guid FileId { get; set; }
public Guid EntityId { get; set; }
public virtual ModelBase Entity { get; set; }
public virtual File File { get; set; }
}
File.cs
[Table("Files")]
public class File : ModelBase
{
// This is the property for which the error occured
private ICollection<FileInEntity> FileInEntities { get; set; } = new List<FileInEntity>();
public IEnumerable<ModelBase> Entities => FileInEntities.Select(e => e.Entities);
}
FilesInEntitiesMap.cs (Relationship Configuration)
builder.HasOne(p => p.Entity)
.WithMany()
.HasForeignKey(k => k.EntityId);
builder.HasOne(p => p.File)
.WithMany()
.HasForeignKey(k => k.FileId);
FileMap.cs
// This is the key to which the error references to
builder.HasMany("FileInEntities")
.WithOne("Entity")
.HasForeignKey("EntityId");
You won't be able to use the base class ModelBase as the object in the mapping class because c# wont know the actual type coming back from the db. You can look at table per hierarchy inheritance, but I'm still not sure you would be able to use that in a mapping table either. Here is a good article
If your Course.cs, Lecture.cs, and Game.cs are the same and the only difference is type, you could combine them into one class and add an enum property to set the type.
public enum EntityType{
Game = 1,
Lecture = 2,
Course = 3
}
public class MyEntity : ModelBase{
private ICollection<FileInEntity> Icons { get; set; } = new List<FileInEntity>();
[NotMapped]
public File Image => Icons.Select(e => e.File).FirstOrDefault();
public EntityType EntityType {get;set;} //course, lecture, or game
}
When you care about the type just use a where clause.
Be sure to use Fluent Api in DbContext's OnModelCreating to determine One to One relationship for this tables. (and be sure again correct reference properties are selected)
Missing parts of your codes.
public class ModelBase
{
[Key]// add for primary key
//set none always for primary keys (because guid has no auto increment)
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid Id { get; set; }
}
[Table("Files")]
public class File : ModelBase
{
//make it public
public ICollection<FileInEntity> FileInEntities { get; set; } = new List<FileInEntity>();
[NotMapped] //set not mapped
public IEnumerable<ModelBase> Entities => FileInEntities.Select(e => e.Entities);
//do it same changes for `Lacture.cs`, `Game.cs` and `Course.cs`...
}

Generic Property and IoC (Autofac) Issues

Im doing an MVC 5 proyect using DI with IoC (Autofac) and the use of Generic Repository for the Data access part.
Now, my controller have an interface as a property injected to it called IProg_II_ModelFactory, this is the concrete class in question:
public class Prog_II_ModelFactory : IProg_II_ModelFactory
{
#region Fields
private readonly IGenericRepository<T> _genericRepository;
#endregion
#region Ctor
public Prog_II_ModelFactory(
IGenericRepository<T> genericRepository
)
{
_genericRepository = genericRepository;
}
#endregion
#region Methods
public Prog_II_Model SetProgIIModelStartUp()
{
var model;
model.FillList = _genericRepository.GettAll();
//Other things to be filled.
return model;
}
}
And my controller is injecting the interface of this class, here is the controller:
public class HomeController : Controller
{
private readonly IProg_II_ModelFactory _prog_II_ModelFactory;
public HomeController(
IProg_II_ModelFactory prog_II_ModelFactory
)
{
_prog_II_ModelFactory = prog_II_ModelFactory;
}
public ActionResult Index()
{
var model = _prog_II_ModelFactory.SetProgIIModelStartUp();
return View(model);
}
}
The problem with this is that Prog_II_ModelFactory must be generic (Prog_II_ModelFactory<T> where T : class)
The problem with this is that, it wont stop there, then it will ask me that the controller should also be generic and on top of that as Im using Autofac, the partial class to for the binding must be generic too and the App_Start itself!.
Is there any way I can use the Generic Interface of My repository without to flow TEntity all the way to App_Start ?
I read that I can use a method? but a lot of people is against this.
Edit:
Maybe instead of the property being Out of T, I have to type it?
for example:
#region Fields
private readonly IGenericRepository<Student> _genericRepository_Student;
private readonly IGenericRepository<Teacher> _genericRepository_Teacher;
#endregion
#region Ctor
public Prog_II_ModelFactory(
IGenericRepository<Student> genericRepository_Student,
IGenericRepository<Teacher> genericRepository_Teacher
)
{
_genericRepository_Student = genericRepository_Student;
_genericRepository_Teacher = genericRepository_Teacher;
}
#endregion
This means I cant do it at runtime, I have to know to which entity at compile time, what if I have 20 tables.
Edit 2:
This is my Model:
namespace Prog_II.Models
{
public class Prog_II_Model
{
#region Fields
public StudentModel Student{ get; set; }
public List<TurnModel> Turns{ get; set; }
public List<CarrerModel> Carrers { get; set; }
public List<StudentModel> Students{ get; set; }
#endregion
#region Ctor
public Prog_II_Model(){}
#endregion
}
public class StudentModel
{
#region Fields
public string Name { get; set; }
public string LastName{ get; set; }
public string NumberId { get; set; }
public string Carrer { get; set; }
public string Turn { get; set; }
#endregion
}
public class TurnModel
{
public string Description{ get; set; }
}
public class CarrerModel
{
public string Description{ get; set; }
}
}
And sure enough I pass a Prog_II_Model in my view:
#using Prog_II.Models
#model Prog_II_Model
public HomeController(Prog_II_ModelRepository modelRepository)...
public class Prog_II_ModelRepository()
{
IGenericRepository<Student> _genericRepository_Student; // get from somewhere
public Prog_II_Model GetModel()
{
var model = new Prog_II_Model();
model.Student = _genericRepository_Student.Get(); // some student
// put the rest of data into your huge all-encompassing model
return model;
}
}
That's just the idea - if you put all your data in a single model object, type-specific repositories are useful inside this single model repo, but it itself has no need to be generic (as opposed to general :)
I think there's something wrong with this One Huge Model approach, but YMMV.
Is your HomeController supposed to handle multiple entity types (Student, Teacher, Janitor, Dog, Cat, ...)? Is your Model flexible enough to handle all and any of them? If yes, use a non-generic repository interface, returning an object or some base entity type. If no, and you have different models per entity type (Dog has Color and Student has Scores), then your domain model asks for controller-per-entity type, inherited, as you said, from a generic controller type.
I don't see any reason for App_Start to be generic.

Data Repository Pattern Design C#

I have an entity class
public partial class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
}
and this entity
public partial class User
{
public User()
{
this.Tokens = new HashSet<Token>();
}
public int UserId { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public virtual ICollection<Token> Tokens { get; set; }
}
I have a repository pattern
public abstract class DataRepositoryBase<T> : IDataRepository<T>
where T : class, new()
{
protected abstract T AddEntity(T entity);
protected abstract T UpdateEntity(T entity);
protected abstract IEnumerable<T> GetEntities();
protected abstract T GetEntity(int id);
}
How can I call repository using generic data repository using some thing like this.<T> just using generic interface not creating new class? Thanks.
_datarepositorypattern.GetDataRepository<IProductRepository>();
_datarepositorypattern.GetDataRepository<IUserRepository>();
what i want, somthing like this.
var obj = _datarepositorypattern.GetDataRepository<IDataRepository<User>>();
//then i can access use
obj.GetEntities();
update
I already created repository
this repository can return
public DataRepositoryBase<Product> ProductRepository => new ProductRepository();
in the service class
private readonly UnitOfWork _unitOfWork;
public ProductServices(UnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
Product[] IProductServices.GetProduct()
{
var repository = _unitOfWork.ProductRepository;
return repository.Get().ToArray();
}
i use this current repository method like this.
what i wanted create dynamic repository so i do not updating my repository again only updating my service class with code like this.
var obj = _datarepositorypattern.GetDataRepository<IProductRepository>();
obj.GetEntities();
just using repository repository pattern i can get dynamic repository product
var obj = _datarepositorypattern.GetDataRepository<IUserRepository>();
obj.GetEntities();
just using repository repository pattern i can get dynamic repository User.
UPDATE
I just googling this is an repositoryfactorypattern can anyone help me.
as you've written it here you need you need new classes. You specify the IProductRepository and IUserRepository
that means you need something like
class UserRepository : DataRepositoryBase<User>, IUserRepository
{
//userrepository code here
}
otherwise you would get something like
_datarepositorypattern.GetDataRepository<IDataRepository<User>>();
expecially since your datarepository class is abstract you need a concrete implementation somewhere
Update: example not using entity specific repositories
If you really want to avoid entity specific repositories you can look at breeze.
you still need something like theis
class MyContext : DbContext {
DbSet<Product> Products {get; set;}
DBSet<User> Users {get; set;}
}
but then you can call a context provider like this
public class DataRepositoryBase<T> : IDataRepository<T> {
readonly EFContextProvider<User> _contextProvider =
new EFContextProvider<User>();
public void SaveEntity(JObject saveBundle) {
_contextProvider.SaveChanges(saveBundle);
}
}
now you can just call new DataBaseObjectRepository() (or have that code in your GetDataRepository method)
maybe that is a place to start. You do need something like DbContext that tells you how to map different types to different tables.

Fluent API EF inheritance and mapping

I've a system with several self referencing entities with one-to-many relationship (parent-child). I'd like to use the common base class for all of those entities:
public class SelfReferencing
{
public SelfReferencing Parent {get; set;}
public ICollection<SelfReferencing> Children {get; set;}
}
and inherit the particular entity from SelfReferencing.
Fluent API mapping requires the reference Properties to be of the defining type, when trying to do following:
modelBuilder.Entity<ConcreteSelfReferencing>()
.HasMany(e => e.Children)
.WithOptional(e => e.Parent);
So, can you help me to find a possibility to make use of inheritance and get the entities mapped?
THX
Note: The example below is known as Table-Per-Hierarchy (TPH) - i.e. all contained in one table. Click on this link for Table-Per-Type (TPT), which has different tables for each type.
When using base types and inherited types, you have to tell EF how to determine the association for a specific inherited type.
Taking your code:
public abstract class SelfReferencing
{
public SelfReferencing Parent { get; set; }
public ICollection<SelfReferencing> Children { get; set; }
}
public class ConcreteSelfReferencing : SelfReferencing
{
}
EF now has to work out whether the sub-class is a ConcreteSelfReferencing or any other type of sub-class. This is determined by a discriminator on the table itself, to which the column is not part of your mapping.
To take another example, similar to I've used in the past:
public abstract class Policy
{
public int Id { get; set; }
public string PolicyNumber { get; set; }
}
public class InsurancePolicy : Policy
{
}
public class CarPolicy : Policy
{
}
The table was structured like this:
| Id | PolicyNumber | Type | ..... |
1 CAR0001 C
2 ISN0001 I
To get EF to result them correctly, you would have:
public class MyContext : DbContext
{
public MyContext() : base()
{
}
public DbSet<Policy> Policies { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
var policyMap = modelBuilder.Entity<Policy>();
// Set up discriminators
policyMap.Map<InsurancePolicy>(p => o.Requires("Type").HasValue("I"))
.Map<CarPolicy>(p => o.Requires("Type").HasValue("C"));
// Notice that `Type` is only used for the discriminators, not an actual
// mapped property
policyMap.HasKey(x=>x.Id);
policyMap.Property(x=>x.PolicyNumber);
}
}
And from your code, you can either do the filtering yourself, or put the filtering in the DbContext. Here is an example from a separate class.
public class PolicyRepository
{
private MyContext context = new MyContext();
public PolicyRepository()
{
}
public IQueryable<InsurancePolicy> GetInsurancePolicies()
{
return this.context.Policies.OfType<InsurancePolicy>();
}
public IQueryable<CarPolicy> GetCarPolicies()
{
return this.context.Policies.OfType<CarPolicy>();
}
}

Entity not resolved in code first DataService with Entity Framework 4.1

I'm making a DataService with Entity Framework 4.1 using Code First POCO objects. I can access the data fine when I create an instance of MyEntityContext and access it directly, however when I try and access the DataService over HTTP it breaks with the following error:
System.ArgumentException: The given name 'Department' was not found in the entity sets.
I've tried all sorts of things but keep coming back to the same problem. Not sure what could be wrong.
Department is the only Entity I am mapping currently. It is defined as follows:
[Table("tb_department")]
public class Department
{
[Key]
[Column("department_no", TypeName = "nvarchar")]
public string ID { get; set; }
[Column("department_name", TypeName = "nvarchar")]
public string Name { get; set; }
}
My data context looks like this:
public class MyEntityContext : DbContext
{
public MyEntityContext(string connStr)
: base(connStr)
{
}
public MyEntityContext()
{
}
public DbSet<Department> Departments { get; set; }
}
And my service looks like this:
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class MyDataService : DataService<MyEntityContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
config.UseVerboseErrors = true;
config.SetEntitySetAccessRule("Department", EntitySetRights.AllRead);
}
protected override MyEntityContext CreateDataSource()
{
MyEntityContext ctx = new MyEntityContext(
Utility.GenerateConnStr
);
return ctx;
}
}
Well it says "Department" was not found in the entity sets and your entity set name is "Departments" so try pluralizing the call.

Categories