Repository that accesses multiple tables - c#-4.0

This model is simplified, only used for demonstration.
In my application got:
Data
public class Product
{
public Guid Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
public class Category
{
public Guid Id { get; set; }
public string Name { get; set; }
}
Repository
public interface IRepository<T>
where T : class
{
T Add(T entity);
T Remove(T entity);
IQueryable<T> GetAll();
int Save();
}
public class ProductRepository : IRepository<Product>
{
public Product Add(Product entity) { ... }
public Product Remove(Product entity) { ... }
public IQueryable<Product> GetAll() { ... }
public int Save() { ... }
}
public class CategoryRepository : IRepository<Category>
{
public Category Add(Category entity) { ... }
public Category Remove(Category entity) { ... }
public IQueryable<Category> GetAll() { ... }
public int Save() { ... }
}
Services
public interface ICategoryService
{
Category Add(Guid gidProduct, Category category);
}
public class CategoryService : ICategoryService
{
public Category Add(Guid gidProduct, Category category){ ... } //Problem here
readonly IRepository<Category> _repository;
public CategoryService(IRepository<Category> repository) //Problem here
{
_repository = repository;
}
}
As I have a repository for each class when I need information from another repository in my service, what should I do?
In the example above, in my service layer I have a method to Add a product (where I found the code for it) and a category.
The problem is that I do a search in the repository of products to recover it, but in my service class category, there is no repository of products., how to solve this problem?

First you need to create a repository for each aggregate root not for each class.
and if you need to access more than one repository in your service, simply you depend on all of them then you can add them as parameters to the constructor for dependency injection.
public CategoryService(CategoryRepository categoryRepository, ProductRepository productRepository)
{
_categoryRepository = categoryRepository;
_productRepository = productRepository;
}

Related

Automapper Object with inside list of object to primitive mapping

I'm trying to create map for automapper to let me map those entity
Entities
public class Entity
{
...
public List<NavigationEntity> Navs { get; set; }
}
public class NavigationEntity
{
public int Id { get; set; }
}
DTO that need to be create with entities
public class EntityDto
{
...
public List<int> NavIds { get; set; }
}
This doesnt seem's to do the job! What could do the job ?
CreateMap<Entity, EntityDto>().ReverseMap();
CreateMap<NavigationEntity, int>().ConstructUsing(x => x.Id);
EDIT
Tried to add
CreateMap< List < SystemsTags >, List< int >>();
but still it doesnt map
First of all, you should rename public List<NavigationEntity> Navs { get; set; } and public List<int> NavIds { get; set; } to the same name. If it is still not working try to also change ConstructUsing to ConvertUsing. And if you need the reverseMap of Entity to EntityDTO you should also add
CreateMap<int, NavigationEntity>().ConvertUsing(x => new NavigationEntity { Id = x });
final code
public class Entity
{
...
public List<NavigationEntity> Navs { get; set; }
}
public class NavigationEntity
{
public int Id { get; set; }
}
public class EntityDto
{
...
public List<int> Navs { get; set; }
}
...
CreateMap<Entity, EntityDto>().ReverseMap();
CreateMap<NavigationEntity, int>().ConvertUsing(x => x.Id);
CreateMap<int, NavigationEntity>().ConvertUsing(x => new NavigationEntity { Id = x });

Automapper Base type, dervied type DTO Mapping

I have the following classes:
public class Entity
{
}
public class Company : Entity
{
}
public class Person : Entity
{
}
public class SomeOther
{
public Entity A { get; set; }
}
And the following DTOs:
public class EntityDTO
{
}
public class SomeOtherDTO
{
public EntityDTO A { get; set; }
}
And the following Mappings:
CreateMap<Person, EntityDTO>();
CreateMap<Company, EntityDTO>();
Is there any way of doing CreateMap<Entity, EntityDTO>() and tell Automapper to use the relevant mapping based on the derived type?

Orchard CMS record mapping from external Assembly

I have an Orchard CMS module that uses external library. And I need to use some classes from that library as part of Orchard records.
For example, external assembly contains class
public class Operation {
public virtual long Id { get; set; }
public virtual string OperationType { get; set; }
}
I have to store it in the database, to use it with Orchard IRepository and use it as part of other Orchard CMS records, such as
public class HistoryRecord {
public virtual long Id { get; set; }
public virtual DateTime Updated { get; set; }
public virtual Operation Operation { get; set; }
}
I was able to get a partial solution, based on Fluet Configuration. However, it works only if the classes correspond to the Orchard's naming conventions.
Here it is:
public class SessionConfiguration : ISessionConfigurationEvents {
public void Created(FluentConfiguration cfg, AutoPersistenceModel defaultModel) {
var ts = new TypeSource(new[] { typeof(OperationRecord) });
cfg.Mappings(m => m.AutoMappings.Add(AutoMap.Source(ts)
.Override<OperationRecord>(mapping => mapping.Table("Custom_Module_OperationRecord"))
));
}
public void Prepared(FluentConfiguration cfg) { }
public void Building(Configuration cfg) { }
public void Finished(Configuration cfg) { }
public void ComputingHash(Hash hash) { }
}
public class TypeSource : ITypeSource {
private readonly IEnumerable<Type> _types;
public TypeSource(IEnumerable<Type> types) {
_types = types;
}
public IEnumerable<Type> GetTypes() {
return _types;
}
public void LogSource(IDiagnosticLogger logger) {
throw new NotImplementedException();
}
public string GetIdentifier() {
throw new NotImplementedException();
}
}

Nhibernate confused by class inheritance and returns mixed results

I have a class with a few properties and some methods
public class Content
{
public int Id { get; set; }
public string Application { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public override bool Equals(object obj) {...}
public override int GetHashCode() {...}
}
With this Fluent NHibernate mapping:
public class ContentMapping : ClassMap<Content>
{
public ContentMapping()
{
Table("vw_all_contents");
CompositeId()
.KeyProperty(x => x.Id, "id")
.KeyProperty(x => x.Application, "application");
Map(x => x.Property1, "property1");
Map(x => x.Property2, "property2");
}
}
Up to here everything works fine.
I now want to populate the same object but with a table a federated table that connects to another database.
So I have:
public class ContentOnProductionDatabase : Content { }
With a mapping:
public class ContenOnProductionDatabasetMapping : ClassMap<ContentOnProductionDatabase>
{
public ContentOnProductionDatabaseMapping()
{
Table("vw_federated_all_contents");
CompositeId()
.KeyProperty(x => x.Id, "id")
.KeyProperty(x => x.Application, "application");
Map(x => x.Property1, "property1");
Map(x => x.Property2, "property2");
}
}
And here is where NHibernate gets really confused and the queries return mixed results from both databases.
The problem goes away if my ContentOnProductionDatabase does not extend Content but instead is a duplicate class like this:
public class ContentOnProductionDatabaseMapping
{
public int Id { get; set; }
public string Application { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public override bool Equals(object obj) {...}
public override int GetHashCode() {...}
}
So now everything is fine but I don't like the fact that there is so much code duplication and it seems to me there must be some sort of Mapping configuration out there to force NHibernate to ignore the inheritance and differentiate the two, especially since they map to different databases.
The repository framework is an inbuilt one handles the session and the queries.
public class ContentRepository : NHibernateRepositoryBase, IContentRepository
{
public ContentRepository(INHibernateContext context, ISettingsManager settingsManager): base(context){ }
public Content ReadContent(int id, string application)
{
using (ISessionContainer container = Context.GetSessionContainer())
{
return
container.AsQueryable<Content>()
.FirstOrDefault(c => c.Id == id && c.Application == application);
// All queries using <Content> return the correct results
}
}
public ContentOnProductionDataBase ReadFederatedContent(int id, string application)
{
using (ISessionContainer container = Context.GetSessionContainer())
{
return
container.AsQueryable<ContentOnProductionDataBase>()
.FirstOrDefault(c => c.Id == id && c.Application == application);
// All queries using <ContentOnProductionDataBase> return the combined results of <Content> and <ContentOnProductionDataBase>
}
}
}
Internally the container.AsQueryable works by invoking this:
public IQueryable<TEntity> AsQueryable<TEntity>() where TEntity : class
{
return LinqExtensionMethods.Query<TEntity>(this.Session);
}
Any ideas how to get rid of the code duplication?
To define the class mapping and the properties only once, you have to define a base class and define the mapping with UseUnionSubclassForInheritanceMapping which will allow you to use independent tables per entity which is derived from that base class.
You don't have to but you should declare your base class as abstract, because it will not have a database representation. So persisting the base class will fail! Meaning, you don't want anyone to use it as an entity, instead use your derived classes...
To do so, create one base, and 2 derived classes which should be stored in one table per class.
public abstract class ContentBase
{
public virtual int Id { get; set; }
public virtual string Application { get; set; }
public virtual string Property1 { get; set; }
public virtual string Property2 { get; set; }
public override bool Equals(object obj)
{
[..]
}
public override int GetHashCode()
{
[..]
}
}
public class Content : ContentBase
{
}
public class ContentOnProductionDatabaset : ContentBase
{
}
The mapping of the base class must call UseUnionSubclassForInheritanceMapping, otherwise nHibernate would combine the classes.
public class ContentBaseMapping : ClassMap<ContentBase>
{
public ContentBaseMapping()
{
UseUnionSubclassForInheritanceMapping();
CompositeId()
.KeyProperty(x => x.Id, "id")
.KeyProperty(x => x.Application, "application");
Map(x => x.Property1, "property1");
Map(x => x.Property2, "property2");
}
}
The subclass mappings just have to define that the base is abstract.
Here you can also define each table name the entity should use.
public class ContentMapping : SubclassMap<Content>
{
public ContentMapping()
{
Table("vw_all_contents");
Abstract();
}
}
public class ContentOnProductionDatabaseMapping : SubclassMap<ContentOnProductionDatabaset>
{
public ContentOnProductionDatabaseMapping()
{
Table("vw_federated_all_contents");
Abstract();
}
}

How do I create a navigation property that can navigate to more than one entity type?

I have the following in my domain classes ( simplified )
public enum JobType
{
SalesOrder = 1,
StockOrder = 2
}
public class SalesOrder : LoggedEntity
{
public string Name { get; set; } // and other fields
}
public class StockOrder : LoggedEntity
{
public string Name { get; set; } // and other fields
}
public class Job : LoggedEntity
{
public int JobType { get; set; } // jobtype enum
public virtual LoggedEntity LinkedEntity { get; set; }
}
My context is as follows;
public class Context : DbContext
{
public DbSet<Job> Jobs { get; set; }
public DbSet<StockOrder> StockOrders { get; set; }
public DbSet<SalesOrder> SalesOrders { get; set; }
}
When I run the migration i get the error described [here][1] So using an abstract entity appears not to work.
My question was, how do I create a navigation property that can navigate to more than one entity type?
If JobType = SalesOrder then I want to navigate to sales order, if JobType = StockOrder then I want to navigate to stock order.
I wanted to use a Table Per Heirarchy Strategy [see TPH here][2]
The trick is to keep EF oblivious of the LoggedEntity class. Remodel your entities according to this example:
public enum JobType
{
SalesOrder = 1,
StockOrder = 2
}
public abstract class LoggedEntity
{
public int Id { get; set; }
public string Name { get; set; } // and other fields
}
public abstract class BaseOrder : LoggedEntity // New base class for orders!!
{ }
public class SalesOrder : BaseOrder
{ }
public class StockOrder : BaseOrder
{ }
public class Job : LoggedEntity
{
public JobType JobType { get; set; } // jobtype enum
public virtual BaseOrder Order { get; set; }
}
public class Tph2Context : DbContext
{
public DbSet<Job> Jobs { get; set; }
public DbSet<BaseOrder> Orders { get; set; }
}
You will see that the migration creates two tables, Jobs and BaseOrders (name to be improved). Job now has a property Order that can either be a SalesOrder or a StockOrder.
You can query specific Order types by
contex.Orders.OfType<StockOrder>()
And you will notice that EF doesn't know LoggedEntity, because
context.Set<LoggedEntity>()
will throw an exception
The entity type LoggedEntity is not part of the model for the current context.
how do I create a navigation property that can navigate
to more than one entity type?
You cannot do so. atleast not now. navigational properties are way of describing relationship between entities. at most, they represent, some sort of sql relationship. so you cannot alter or define such a relationship on the fly. you have to define it before hand.
Now in order to do that, you have to define separate navigational property for your separate conditions i.e.
public class Job : LoggedEntity
{
public int JobTypeSales { get; set; }
public int JobTypeStock { get; set; }
public virtual SalesOrder SalesOrder { get; set; }
public virtual StockOrder StockOrder { get; set; }
}
and then link them in configuration in modelbuilder through fluent API.
HasRequired(s => s.SalesOrder)
.WithMany()
.HasForeignKey(s => s.JobTypeSales).WillCascadeOnDelete(true);
HasRequired(s => s.StockOrder)
.WithMany()
.HasForeignKey(s => s.JobTypeStock).WillCascadeOnDelete(true);
and
as for your error "Sequence Contains No Elements"
this error comes, when the Linq query that you specified, is using either .First() or .Single(), or .ToList() and query returned no data.
so to avoid it use, .FirstOrDefault() or SingleOrDefault().
obviously with proper null check.

Resources