I am not sure in terms of exact technical specification for this problem to me but in simple words I am trying to create a wrapper/extension method around to save my entities.
So I added new Entity Data Model (.edmx) file to my project. That generates DbSet(s) like this-
public partial class SUContextContainer : DbContext
{
public SUContextContainer()
: base("name=SUContextContainer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Category> Categories { get; set; }
public DbSet<Gallery> Galleries { get; set; }
public DbSet<SuperUser> SuperUsers { get; set; }
public DbSet<UserType> UserTypes { get; set; }
}
Now here I am trying to wrap this into an extension method for database operations like (save, delete, update etc..)
I tried creating it as -
public static void Save(this EntityObject objEntity)
{
try // Update Record
{
((IObjectContextAdapter)Global.Context).ObjectContext.ObjectStateManager.ChangeObjectState(objEntity, EntityState.Modified);
Global.Context.SaveChanges();
}
catch (OptimisticConcurrencyException) // Insert Record
{
((IObjectContextAdapter)Global.Context).ObjectContext.ObjectStateManager.ChangeObjectState(objEntity, EntityState.Added);
Global.Context.SaveChanges();
}
}
This method is attached to EntityObject types. Where .edmx code which it generates are of type DbContext.
So Whenever I try to save some entity with this helper method it never finds out.
var galleryEntity = new Gallery {
IsActive = true,
CategoryId = model.CategoryId,
};
galleryEntity.Save(); // the save method is not found.
I tried above method to change in -
public static void Save(this DbSet objEntity)
But this also doesn't seem to take as extension method.
What am I doing wrong.
So Whenever I try to save some entity with this helper method it never
finds out.
It will not, because gallery is just a class and is not inherited from EntityObject.
I don't suggest adding inheritence or modifiying autogenerated classes.
Use power of partial classes:
You can create patial classess for your models with interface.
public partial class Gallery : IEntity
{
//This is your class different than auto generated class by Ef.
}
Also you shouldn't use try catch for decision. That's why you should seperate update and create and make decision on upper level (without try catch).
So your extension methods should be like this.
public static int Update<T>(this T entity) where T : IEntity
{
using(var dbContext=new SUContextContainer())
{
var entry = dbContext.Entry(entity);
dbContext.Set<T>().Attach(entity);
entry.State = EntityState.Modified;
return dbContext.SaveChanges();
}
}
public static int Create<T>(this T entity) where T : IEntity
{
using(var dbContext=new SUContextContainer())
{
dbContext.Set<T>().Add(entity);
return dbContext.SaveChanges();
}
}
Your extension method will only apply to types that inherit from EntityObject.
You will either need to make all of your entity classes inherit from this EntityObject class or create another extension method that applies to the correct type.
Typically when using these kind of persistence patterns you would create an entity base class
public class Entity
{
public int Id { get; set; }
}
and each entity type inherits from it
public class Gallery : Entity
{
public int Name { get; set; }
}
Then you can have common methods that you use across entity types:
public static void Save(this Entity entity);
Related
I have a database with 3 tables for classes that all implement my IMeetingEntityEntityBaseClass.cs
public class BookingAppDbContext : DbContext
{
public BookingAppDbContext(DbContextOptions<BookingAppDbContext> options) : base(options)
{
}
public DbSet<MeetingSpace> MeetingSpaces { get; set; }
public DbSet<Employee> Employees { get; set; }
public DbSet<Booking> Bookings { get; set; }
}
I started off with only the MeetingSpaces table and was trying to make my database operation methods generic so they could be reused for the other two tables. However they (and my ASP.NET pages) rely on the Id to identify the meeting space. My problem is that I cant figure out how to make the GetById class generic. For the MeetingSpace table query it is as follows:
public MeetingSpace GetById(int id)
{
return db.MeetingSpaces.Find(id);
}
I attempted to use the Find overload with the type specified but the typeof(T) returns 'object', throwing an error that type 'object' cannot be converted to 'T'.
public T GetById<T>(int id) where T : IMeetingEntityBaseClass
{
return db.Find(typeof(T), id);
}
You need to use the DbContext.Set method. Also, you need to add a type constraint since the Set method relies in it (as do all EF entities.) For example:
public T GetById<T>(int id)
where T : class, IMeetingEntityBaseClass
{
return db.Set<T>().Find(id);
}
I have .net core rest api, which contains hybrid structure in which it only contains repositories and not the service layer.
Now, there is one issue that I am facing with base repository and main structure. let me explain the issue first.
So, consider one entity. let's say Product and below is the definition for that entity. this entity has one base class called FullAuditedEntity.
[Table(name: "Products")]
public class Product : FullAuditedEntity
{
public string Name { get; set; }
}
public class FullAuditedEntity: IFullAuditedEntity
{
public FullAuditedEntity() { }
[Key]
public virtual int Id { get; set; }
}
public interface IFullAuditedEntity
{
int Id { get; set; }
}
The Base repository and it's interfaces are as below.
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IFullAuditedEntity, new()
{
private readonly ApplicationContext context;
public EntityBaseRepository(ApplicationContext context)
{
this.context = context;
}
public virtual IEnumerable<T> items => context.Set<T>().AsEnumerable().OrderByDescending(m => m.Id);
public virtual T GetSingle(int id) => context.Set<T>().FirstOrDefault(x => x.Id == id);
}
public interface IEntityBaseRepository<T> where T : class, new()
{
IEnumerable<T> items { get; }
T GetSingle(int id);
}
So, my Product repository will be like this.
public interface IProductRepository : IEntityBaseRepository<Product> { }
public class ProductRepository : EntityBaseRepository<Product>, IProductRepository
{
private readonly ApplicationContext context;
public ProductRepository(ApplicationContext context) : base(context: context)
{
this.context = context;
}
}
Now, up-to here everything is good, I can access this repository in controllers and can perform the actions that are listed in base class.
Issue I am facing : So with this structure, If I tries to add any new entity without FullAuditedEntity (see Product entity above, I have base class full audited entity over there), my structure of repository fails and it gives error.
let's say if I tries to add new entity Implementation, and this new entity has a random Id, so I do not want to inherit the FullAuditedEnitity base class. now in this case most of the thing will work fine but when I will try to create repository for Implementation entity, it will give generic error. see below snap of that.
What I tried so far...
I was thinking to create a parallel Base repository which does not inherit FullAuditedEntity as a generic class but I am not sure if it's best practice or not. also my concern is that what if I am doing any mistake in my current structure of repository pattern and Dependency injection?
Any help world be best and really appreciated.
Thank you in advance for your time.
Repositories are usually mapped to database tables. Database table should always have some column which can uniquely identify the row in table and it is common practice to call this column as Id. So you correctly implemented your FullAuditedEntity as there is Id property. However, your Id has always type of int. I suggest you to use the following construction and then your Id would be any type of struct such as int, decimal, Guid, and etc:
/// <summary>
/// Abstraction of the Entity
/// </summary>
public interface IEntity
{
object Id { get; set; }
}
/// <summary>
/// Base class for IDs
/// </summary>
public abstract class Entity<T>: IEntity where T: struct
{
public T Id { get; set; }
object IEntity.Id
{
get { return Id; }
set {
Id = (T)value;
}
}
}
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntity, new()
{
// The code is omitted for the brevity
}
In addition, try to avoid entities without Id like Implementation as in future you will have to figure out how to find rows without Id in your database table.
UPDATE:
If you do not want to inherit FullAuditedEntity, then you can create BaseRepository<T> and then derive it in the EntityBaseRepository.
public abstract class BaseRepository<T> : IEntityBaseRepository<T> where T : class, new()
{
public virtual IEnumerable<T> items => throw new NotImplementedException();
public virtual T GetSingle(int id)
{
throw new NotImplementedException();
}
}
public class EntityBaseRepository<T> : BaseRepository<T> where T : class
, IFullAuditedEntity, new()
{
public override IEnumerable<T> items => base.items;
public override T GetSingle(int id)
{
return base.GetSingle(id);
}
}
and then your Implementation repository:
public interface IImplementationRepository : IEntityBaseRepository<Implementation> { }
public class ImplementationRepository: BaseRepository<Implementation>
, IImplementationRepository
{
public override Implementation GetSingle(int id)
{
return base.GetSingle(id);
}
}
UPDATE 1:
In my view, it is better to use services(Service layer) which consume ITRepository<T>.
Because it gives to you new abilities such as:
Add some calculations on data which got by Repository
Remap your entities which are pulled by the Repository
It is one additional layer of decoupling. So when you edit your service layer, then you don't need to edit Repository layer and then recompile your assembly
I am trying to design a webservice that use the Knowntype attribute for the Derived class.
The reason i am using the knowtype is to have a single endpoint for all derived classes.
[DataContract]
[KnownType(typeof(Derived))]
[KnownType(typeof(DerivedTwo))]
//And other derived types
class Base
{
string Name {get; set;}
}
[DataContract]
class Derived :Base
{
string WorkedOn {get; set;}
}
[DataContract]
class DerivedTwo :Base
{
string CompletedOn {get; set;}
}
public class TestContext :DbContext
{
//ctor of context
public Dbset<Base> Base {get; set;}
public Dbset<Derived> Base {get; set;}
}
public class Repository<T> where T:Base
{
private readonly TestContext _testContext;
public Repository()
{
_testContext = new TestContext();
}
public void Add(T input)
{
var kew = _testContext.Set<T>().Add(input);
}
public void Save()
{
_testContext.SaveChanges();
}
}
public Interface IService
{
[OperationContract]
void Add(Base base);
}
public class Service :IService
{
public void Add(Base base)
{
var repository = new Repository<typeof(base)>();
repository.Add(base);
repository.Save();
}
}
When the client calls this endpoint and passes a derived class
i want a entry to be added to the derived class table (TPC -- table for concrete class)
Is this even possible???
what are my potions to design a webservice like this.
Edited to answer
I am pretty new i do not know where i am supposed reply and the edit time for the comment below expired so i am doing it here.
I tried what you have suggested and EF is inserting the properties from the Base class into the Base class table and from the derived class in to the corresponding table. Is there some way i can force it to insert all of the data into the derived table.
Edit2
I figure out the answer with the help of google i need to use the MapInheritedProperties on the DbSet.
Thank You for you help i will mark this answered
The only thing that's wrong with your existing code is that var repository = new Repository<typeof(base)>(); is not a valid form of a closed generic.
Given what you've shown, the design of your repository class is suspect - it would make more sense implemented as
public class Repository
{
private readonly TestContext _testContext;
public Repository()
{
_testContext = new TestContext();
}
public void Add<T>(T input) where T : Base
{
var kew = _testContext.Set<T>().Add(input);
}
public void Save()
{
_testContext.SaveChanges();
}
}
Which would allow your service implementation to be
public void Add(Base input)
{
var repository = new Repository();
repository.Add(input);
repository.Save();
}
and work as expected.
it seems if i use an custom class as base of an entity,the ObjectContext.CreateObjectSet will fail with stackoverflow exception
code is:
// This is generated by EF4 and i modify it to my custom class
public partial class EntityA : GClass<EntityA>
{
......
}
public partial class TestEntities : ObjectContext
{
public ObjectSet<EntityA> EntityAs
{
get
{
if ((_EntityAs == null))
{
// here will throw stackoverflow exception
_EntityAs = base.CreateObjectSet<EntityA>("EntityAs");
}
return _EntityAs;
}
}
private ObjectSet<EntityA> _EntityAs;
}
// This is custom class
public partial class EntityA
{
}
// This is my custom base class
public class GClass<T> : EntityObject where T : class
{
public virtual string GetStr()
{
return "GClass";
}
}
I recommend creating an interface for your entity objects instead of changing the base class. Generated code should not be modified.
Update: Due to unexplained downvotes, I'm adding the code below, which spells out precisely what I mean:
// Generated by EF4
public partial class EntityA : EntityObject
{
...
}
// Interface defined in another file
public interface IGClass<T> where T : IGClass<T>
{
string GetStr();
}
// Automatically generated by T4 template
public partial class EntityA : IGClass<EntityA>
{
public virtual string GetStr()
{
return "GClass";
}
}
The resulting code does use CRGP, but does so via an interface instead of a base class.
More info on T4 templates is here.
I am extending this DataContext entity, which looks sort'a like this:
namespace Entities
{
public class User
{
public Int32 Id { get; set; }
public String Username { get; set; }
}
}
.. Like so:
public class User : Entities.User
{
new public Int32 Id
{
get { return base.Id; }
}
public void Insert()
{
using (var dc = new DataContext())
{
/*
The "this" keyword should match the type that InsertOnSubmit() expects.
And it does. But I get the following error:
System.NullReferenceException: {"Object reference not set to an instance
of an object."}
*/
dc.Users.InsertOnSubmit(this); // Exception occurs here
dc.SubmitChanges();
}
}
}
I am using the custom User class like so:
var u = new User { Username = "Test" };
u.Insert();
What I don't get is this: I have instantiated the class, so why am I getting a NullReferenceException?
Update:
Extending entity class: overriding a property with an enumerator while still being able to use the "this" keyword on the Insert/Update and DeleteOnSubmit methods on a DataContext instance
enum AccessLevels
{
Basic,
Administrator
}
namespace Entities
{
public class User
{
public Int32 Id { get; set; }
public String Username { get; set; }
public Int32 AccessLevel { get; set; }
}
}
How would I extend or alter the above entity class and implement the AcessLevels enumerator, replacing the AccessLevel property?--this without altering the signature of the entity class, so I'm able to use the "this" keyword on Insert/Update and DeleteOnSubmit methods on a DataContexts.
You can't extend LINQ-to-SQL entity types in this way via inheritance - you should instead use a partial class to add extra methods to the existing generated entity. Because LINQ-to-SQL supports inheritance (for discriminated tables, etc), it expects an exact match to a known entity type - not unexpected subclasses.
i.e.
namespace Entities {
partial class User {
/* your extra method(s) here */
}
}
In the above, this is combined with the partial class in the designer.cs to create you type.
The other way to do this (if partial class isn't an option) is via an extension method.
static class EntityExtensions {
public static void SomeMethod(this User user) {...}
}
If there are methods common between types, you can do this by declaring an interface, using extension methods on that interface, and using partial classes to add the interface to the specific types:
namespace Entities {
partial class User : IFunkyInterface {
/* interface implementation, if necessary */
}
}
static class EntityExtensions {
public static void SomeMethod(this IFunkyInterface obj)
{...}
}
or if you need to know the type:
static class EntityExtensions {
public static void SomeMethod<T>(this T obj)
where T : class, IFunkyInterface
{...}
}
Re the enum edit (added as a second answer to keep things simple)...
Firstly - is there a direct 1:1 mapping between the enum and the values? For example, if Basic is 7 and Administrator is 12, then:
enum AccessLevels
{
Basic = 7,
Administrator = 12
}
Then change the type of that property in the dbml (via the designer) from int to your (fully-qualified) enum: Entities.AccessLevel. LINQ-to-SQL supports enums either as direct integer mappings, or as direct string mappings.
If this isn't possible (more complex scenerios), you can isolate the storage (int) and object-oriented (enum) models; rename the property to AccessLevelStorage (or anything else you like), and in a partial class do the mapping:
partial class User {
public AccessLevel AccessLevel {
get {
switch(AccessLevelStorage) {
case 1: return AccessLevelStorage.Foo;
... etc
default: ...throw an exception?
}
}
set {
switch(value) {
case AccessLevel.Foo: AccessLevelStorage = 1; break;
...etc
default: ...throw an exception?
}
}
}
The only caveat here is that LINQ queries will only work against the storage properties - not the custom mapped property. If you do your queries at the level that declares the context, you can change the access of the storage property to internal - but if you do queries outside of this assembly you'll need to leave is public. You might want to add [Browsable(false)] to stop it appearing in UI models, but that is about it.