I am in the process of designing my data access code, the data is going to be stored in RavenDB, and I am trying to see if my current design has too much abstraction with the number of interfaces I am going to have.
I am going to have DTOs that will just hold the data and then I am going to have Entity (or Model, Business, or whatever you call them) objects that will have extra functionality. I am also going to have an interface per entity defining what data it needs to have. So for example:
interface IUser
{
string Id { get; }
string Username { get; }
string Password { get; }
bool ResetPassword { get; }
}
class UserDTO : IUser
{
public string Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public UserDTO()
{
Id = null;
Username = null;
Password = null;
ResetPassword = false;
}
}
class User : IUser
{
public string Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public User()
{
Id = null;
Username = null;
Password = null;
ResetPassword = false;
}
public User(IUser user)
{
Id = user.Id;
Username = user.Username;
Password = user.Password;
ResetPassword = user.ResetPassword;
}
public ResetPassword()
{
Id = null;
Username = null;
Password = null;
}
}
The reason I want to have an interface for each entity is because I want to make sure the both EntityDTO and Entity have the required shared data.
Now for retrieving and saving the data, I am going to use the repository pattern. I am going to have one generic interface called IDataRepository and then each entity is going to have is own repository interface. For example:
interface IDataRepository<T>
{
bool Save(T entity);
bool Delete(T entity);
}
interface IUserRepository : IDataRepository<IUser>
{
IUser Load(string key);
IUser LoadByLogin(string username, string password);
}
class UserRepository : IUserRepository
{
bool Save(T entity)
{
//save code
}
bool Delete(T entity)
{
//delete code
}
IUser Load(string key)
{
//load code
}
IUser LoadByLogin(string username, string password)
{
//load code
}
}
The reason I want to have a repository interface for each entity is so that I can if I need to use different data storage options for different entities.
Do this seem like too much abstraction?
I think this model has its goods and bads. If your DTO is going to match the Entity, why have the DTO? In some systems this is something that needs to happen and in others it is a waste of time. Entities usually have baggage and dont serialize well depending on the ORM. If you use that interface the mapping between the DTO and Entity can be done with AutoMapper and should work well every time. Do you need to have the Entity class live in an separate DLL from the DTO? If so I think your model works.
Yes, this is too much abstraction. Specifically, I do not see a need for the UserDTO class of objects.
IUser itself should define the complete interface necessary for the data repository to do its job. If the repository is handed an object that implements IUser, like User for example, then it should be able to store the data just fine. And, since the interfaces are defined at the lowest level, omitting the UserDTO object will not create any dependency problems.
What you have, in essence, seems fine to me.
However, unless you have further distinction between the DTO and Entity classes and a clear need to separate the two, I would simply have User extends UserDTO and remove the interface all together. That way, you remove the code redundancy that you currently have.
Related
I have a large class that used to hold different information for two different sets of the same class. For example, say a receipt that can be a customer receipt or an internal receipt. All this information is in one giant class because that's how the database is structured, but I want to separate the classes so that I would have a receipt class that holds all the shared information, a customer receipt class, and an internal receipt class. They could share an interface, but the implementations would be different, and that is what is confusing me.
Should there be two separate interfaces that get implemented? So I would have an ICustomerReceipt and IInternalReceipt? I thought there should be one interface with say a Get() and Save() method, and based on the implementation if it's a customer or internal receipt, I get the information specific to the class. I'm a bit lost here.
public class Receipt {
public int ID { get; set; }
public int ReceiptNumber { get; set; }
public List<string> Items { get; set; }
}
public class CustomerReceipt : Receipt {
public string CustomerNumber { get; set; }
public string CustomerEmail { get; set; }
public string CustomerOption { get; set; }
}
public class InternalReceipt : Receipt {
public string InternalNumber { get; set; }
public string InternalEmail { get; set; }
public string InternalOption { get; set; }
}
public interface IReceiptRepository {
public Receipt Get(int id);
public Receipt Add(Receipt receipt);
}
public CustomerReceiptRepository : IReceiptRepository {
public CustomerReceipt Get(int id) {
// get information about customer receipts here
}
}
public InternalReceiptRepository: IReceiptRepository {
public InternalReceipt Get(int id) {
// get information about internal receipts here
}
}
Basically I just want to return the correct Receipt to a view model in my controller that just has the generic 'ReceiptNumber' or 'ReceiptEmail'. I know it's not the best example, but it's the only one I could come up with.
Don't get tripped up on trying to force two similar things to share a single abstraction (base class or interface). So, I'd recommend what you suggested: create two separate interfaces.
Remember, the point of polymorphism is so you don't have to know what specific (derived/implemented) type an instance is if you're only looking for an instance of the base type/interface. That's it. If you don't need that, then jumping through hoops to force two similar classes to share a base is not worth it.
public interface IReceiptRepository {
public Receipt Get(int id);
public Receipt Add(Receipt receipt);
}
public CustomerReceiptRepository : IReceiptRepository {
public Receipt Get(int id) {
// get information about customer receipts here
return new CustomerReceipt();
}
}
public InternalReceiptRepository: IReceiptRepository {
public Receipt Get(int id) {
// get information about internal receipts here
return new InternalReceipt();
}
}
Inheritance can be represented on the database in different ways and there are some strategies depending on the ORM you are using.
At the end of the day, using one of the strategies, you can base your repository on the base class and let the ORM act as a proxy to resolve the instance you need, or try to recreate yourself, at the level of the repository, based on a discriminator field, the instances you need
Receipt
ID
ReceiptNumber
CustomerNumber
CustomerEmail
CustomerOption
InternalNumber
InternalEmail
InternalOption
DISCRIMINATOR_FIELD
(most of the ORM do this translation for you), but for you to get the idea, you can keep only one repository to treat all the classes as Receipt and keep your hierarchy as you have it.
public interface IReceiptRepository {
public Receipt Get(int id);
public Receipt Add(Receipt receipt);
}
public CustomerReceiptRepository : IReceiptRepository {
public Receipt Get(int id) {
var rec = DbContext.Table.Receipt.FirstOrDefault(r => r.id = id);
if(rec.DiscriminatorField == 1) //CustomerReceipt
{
return new CustomerReceipt
{
ID = ...
ReceiptNumber = ...
CustomerNumber = ...
CustomerEmail = ...
CustomerOption = ...
}
}
//all other cases are InternalReceipts
return new InternalReceipt
{
ID = ...
ReceiptNumber = ...
InternalNumber = ...
InternalEmail = ...
InternalOption = ...
}
}
}
The same thing for the Add method, just fill only the fields you need for that object. This composition is basing everything on a discriminator field. I am not suggesting you implement your solution in that way, but with that, you still get on your ViewModel the generic receipt. My suggestion is that you read more about the ORM you are using an how you can represent inheritance there(maybe you are using database first instead of code first and you will need to handle the things manually, because the database was not designed on that way and you need to take a similar approach of what I suggested. But if you have the chance to create your POCO classes and create the database, definitely it deserves to take a look at how they implement the inheritance.
Here I am attaching a link of how this problem is addressed on EntityFramework 6
Inheritance Strategy in Entity Framework 6
Hope this helps
I am using a generic repository and Entity Framework. I can update one of the classes normally, but I'm having trouble updating the relationship between them.
I'm also using lazy loading, AutoMapper and a service layer to isolate the domain.
public class DetalhesDoArquivoViewModel
{
public DetalhesDoArquivoViewModel()
{
Id = Guid.NewGuid();
}
[Key]
public Guid Id { get; set; }
public string FileName { get; set; }
public string Extension { get; set; }
public Guid FormularioId { get; set; }
public virtual FormularioDoUploadViewModel DescricaoDoUpload { get; set; }
}
public class FormularioDoUploadViewModel
{
public FormularioDoUploadViewModel()
{
Id = Guid.NewGuid();
}
[Key]
public Guid Id { get; set; }
[Required(ErrorMessage = "Digite um nome")]
[Display(Name = "Nome")]
[MaxLength(100)]
public string Nome { get; set; }
[Required(ErrorMessage = "Entre com uma descrição")]
[Display(Name = "Descrição")]
[MaxLength(500)]
public string Descricao { get; set; }
public virtual IEnumerable<DetalhesDoArquivoViewModel> DetalhesDoArquivo { get; set; }
}
My Update repository
public virtual TEntity Atualizar(TEntity obj)
{
var entry = Db.Entry(obj);
Dbset.Attach(obj);
entry.State = EntityState.Modified;
SaveChanges();
return obj;
}
My service class:
public class UploadAppServices : BaseService, IUploadServices
{
private readonly IFormularioUploadRepository _formularioUploadRepository;
private readonly IDetalhesDoArquivoRepository _detalhesDoArquivoRepository;
// Update
public FormularioDoUploadViewModel Atualizar(FormularioDoUploadViewModel formularioDoUploadViewModel)
{
var form = Mapper.Map<FormularioUpload>(formularioDoUploadViewModel);
_formularioUploadRepository.Atualizar(form);
Commit();
return formularioDoUploadViewModel;
}
//getById
public FormularioDoUploadViewModel ObterPorId(Guid id)
{
return Mapper.Map<FormularioDoUploadViewModel>(_formularioUploadRepository.ObterPorId(id));
}
}
My controller:
public class FormularioDoUploadController : BaseController
{
private ApplicationDbContext db = new ApplicationDbContext();
private IFormularioUploadRepository _formularioUploadRepository;
private IUploadServices _uploadServices;
public ActionResult Edit(Guid id)
{
var formularioDoUploadViewModel = _uploadServices.ObterPorId(id);
if (formularioDoUploadViewModel == null)
{
return HttpNotFound();
}
return View(formularioDoUploadViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(FormularioDoUploadViewModel formularioDoUploadViewModel)
{
if (ModelState.IsValid)
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
DetalhesDoArquivoViewModel detalhesDoArquivo = new DetalhesDoArquivoViewModel()
{
FileName = fileName,
Extension = Path.GetExtension(fileName),
FormularioId = formularioDoUploadViewModel.Id,
};
var path = Path.Combine(Server.MapPath("~/App_Data/Upload/"), detalhesDoArquivo.Id + detalhesDoArquivo.Extension);
file.SaveAs(path);
}
// Update
_uploadServices.Atualizar(formularioDoUploadViewModel);
return RedirectToAction("Index");
}
}
return View(formularioDoUploadViewModel);
}
Automapper is great for mapping entity to view-model, but I would avoid using it to map from a view-model to entity. This may seem convenient, but you are effectively unconditionally trusting the data received from the client and overwriting your database data. This means you have to send 100% of your entity domain model to the client, revealing more about your domain structure than you need to, and then accept that expanded domain model which can contain alterations that your client application does not intend to make. (intercepting the post to the server in the browser debugger and altering values in the object posted back to the server)
Submit actions should be coded to:
Validate that the current session user has permission to modify the record(s) identified by the submit request.
Limit the update to specific values provided in the request.
Validate those specific values.
Disconnect the user session and notify administrators if any of the above is violated.
In some cases, such as adding a new entity, the payload will effectively be a complete entity and potentially some related details. This still needs to be validated against the known data state. In other cases where you provide an action that updates an entity, the model posted back should merely contain the ID of the entity being updated, and the specific values the client is allowed to update. (not the entire, modified entity)
By passing entities, or view models that map directly to entities for a method intended to update some aspects of the entity, I can:
Re-assign that entity to someone else.
Use the request to attempt to assign another random entity to myself.
Negate or otherwise change any and all data recorded in that entity.
Do not trust anything received from the client.
This issue also presents a concurrent access issue where your system is adopting a "last in wins" scenario. Between the time you provided the entity/view model and the time you submit the view model back to the server, that entity data may have changed. By mapping the data into a new entity class, attaching, marking modified, and saving, you overwrite the data without any consideration as to whether the data was stale.
To avoid the issue you are seeing, and the security/stale issues, you should load the entity from the context on the Update post call, validate the authorization for the current user, check the row version # or timestamp to ensure the record isn't stale, validate your updated details, then, once you're absolutely sure that the data in your view model presents no risk to your entity, you can use automapper's .Map(source, detination) to copy the values across. If you need to update related entities against related view models, then as long as you .Include() those related entities when you retrieve the entity from the context, then the .Map() call should handle the related data.
I just wondering if you can store a function in a model (CRUD transactions)
that will look something like this:
My Existing code:
public class tbluser
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
[Required(ErrorMessage = "Username is required")]
public string username { get; set; }
[Required(ErrorMessage = "Password is required")]
public string password { get; set; }
public static List<tbluser> list()
{
using (var db = new sample())
{
var user = db.tbluser.ToList();
return user;
}
}
}
What i want:
public class tbluser:DbContext
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int id { get; set; }
[Required(ErrorMessage = "Username is required")]
public string username { get; set; }
[Required(ErrorMessage = "Password is required")]
public string password { get; set; }
public static List<tbluser> list()
{
return this.toList();
}
}
I just want to ask also if that method of implementing Entity Framework is ok.
Here is a quick example of how you might setup a simple Code First implementation to get started.
First, define your User model. The Key attribute on an integer type automatically configures the identity property for you. Then, you may want an index on username if you plan to do frequent lookups by username (to get user details or to validate a password).
public class User
{
[Key] // Becomes identity by default
public int Id { get; set; }
[Index("IX_User_Username", IsUnique = true)]
public string Username { get; set; }
public string Password { get; set; }
}
Then, you can define
public class AppDataContext : DbContext
{
public AppDataContext() : base("name=DBConnection") { }
public DbSet<User> Users { get; set; }
}
You will just need to be sure there is a connection string in your config file to match the name passed there.
<connectionStrings>
<add name="DBConnection" providerName="System.Data.SqlClient"
connectionString="Data Source=instancePath;Initial Catalog=dbName;Integrated Security=true;MultipleActiveResultSets=True" />
</connectionStrings>
This would now allow you to create repos such as this:
public class UserRepo : IDisposable
{
public Lazy<AppDataContext> _db = new Lazy<AppDataContext>(() => new AppDataContext());
public IQueryable<User> Get() => _db.Value.Users.AsQueryable();
public IList<User> GetAll() => _db.Value.Users.ToList();
public void Dispose()
{
if (_db.IsValueCreated)
_db.Value.Dispose();
}
}
So then you can either use the repo or the context directly.
// Use the repo
using (var userRepo = new UserRepo())
{
var allUsers = userRepo.GetAll();
var user = userRepo.Get().FirstOrDefault(m => m.Username == "myUsername");
}
// Or just use the data context
using (var db = new AppDataContext())
{
var allUsers = db.Users.ToList(); // Get all users
var user = db.Users.FirstOrDefault(m => m.Username == "myUsername");
}
For more information, here are some useful links with great details:
Simple Example
Data Annotations
Initializer Config
Migrations
Code like this is going to be heavily problematic.
In the first example you are tightly coupling an instance of a DbContext to an entity. Calling tblUser.list() will return a list of User entities, but these will now be outside of the scope of a DbContext. (Due to the using() block closure) This means that any lazy load calls to retrieve related entities will fail and you cannot persist any changes to the entities until they are re-attached to another DbContext. This gets very messy, very fast.
In the second example you would be extending a DbContext, meaning each "entity" is effectively scoping a DbContext use to populate instances of itself. You can't just "static" wrap the method because that wouldn't have visibility to the non-static DbSets inherited from DbContext.
This would be horrible in terms of performance, and from a code perspective would look plain weird:
I.e.
using (var user = new tbluser)
{
var users = user.list(); // not static.
// .. Do stuff..
}
To make it static would be problematic because a DbContext would need to be static-scoped inside tbluser
public class tbluser
{
private static MyContext _context = new MyContext();
// ...
public static List<tbluser> list()
{
return _context.tblusers.ToList();
}
}
And this may still have issues, such as how the static instance is disposed, before it was remotely functional but I certainly cannot recommend an approach like this.
Instead, use the DbContext as it is intended. Look at IoC containers like Unity or Autofac to manage the lifetime scope for for the DbContext and inject an instance as a dependency into classes that need it, or at a minimum wrap it in a using() {} block and treat it like an repository with it's DbSets.
There are lots of examples of using the DbContext effectively, using Repositories and Unit of Work patterns with dependency injection. Master these before attempting to spin up something unique. Future developers looking at your code will thank you. :)
There is one famous principle called "Separation of Concerns" that will get very angry if you do this. My advice is to keep the code simple, meaningful and loosely coupled.
My domain class:
public class Address
{
[Key]
public virtual string AddressId { get; set; }
public virtual string Address { get; set; }
}
In my MVC controller I want to check the given Address exist, before I insert.
public ActionResult Create(Address address)
{
if (ModelState.IsValid)
{
if (db.Addresses.Any(a => a.AddressId == address.AddressId)) // how I do it now
{
ModelState.AddModelError(string.Empty, "Address Id already exists!");
}
else
{
db.Addresses.Add(address);
db.SaveChanges();
return RedirectToAction("Index");
}
}
}
But there are lot of other domain classes in my project and I want to do the same check again and again.
My question is I want to write a generic method in my Db context class to perform this check. (looks like below or similar)
public bool Exists(object) {
// return true if exist
}
i.e. a method which I can call like this:
db.Exists(address)
Thanks!
You could use generics and do something like the following:
public class YourDbContext : DbContext
{
...
public bool Exists<TEntity>(object id)
where TEntity : class
{
var dbSet = Set<TEntity>();
var entity = dbSet.Find(id);
return entity != null;
}
Which you'd then use like:
db.Exists<Address>(address.AddressId);
Using Find isn't the most efficient way to handle this, but it has the key benefit that you're not required to know what the actual primary key property on the class is, which would greatly complicate this method. For example, Address has AddressId, but Foo might have FooId.
UPDATE
Since ultimately this just uses Find under the hood, you just have to modify the method slightly to be able to take multiple parameters. Find handles composite keys by allowing one more parameters to be passed to it. But bear in mind, the the order matters and must align with the key order you specified when configuring your entity.
public bool Exists<TEntity>(params object[] keys)
where TEntity : class
{
var dbSet = Set<TEntity>();
var entity = dbSet.Find(keys);
return entity != null;
}
I have a site that authenticates using Active Directory. I am using the Entity Framework and I need to store references to this users. I don't want save AD users in the database. One way is to store the user guid as a String in an Entity.
class Entity
{
String UserGUID ;
}
Is it possible to something like this:
class Entity
{
UserPrincipal user;
}
Instead of passing the string GUID pass an object and make the Entity Framework treat the association somewhat as if it the UserPrincipal object was an entity. It doesn't have to be the class UserPrincipal, it could be a another class. I would like to deal with objects rather than strings. Querying Active directory is not a problem.
In summary, I would like to be able to associate an entity with a non-entity class by storing a String GUID in the database but loading it as an object.
[UPDATE]
Many classes might have multiple associations with the AD users and it can vary so a base class is not a solution. For example, I might have a class like this:
class Message
{
public User Sender;
public User Recipient;
public List<User> MentionedUsers;
}
This is not a class I am using but it illustrates my point. Ideally the User guid would be stored in the Message entity table but be loaded as a User just like the Entity Framework does with other entites.
I am thinking creating User as a wrapper entity class to the GUID and retrieve properties with static methods but I would like to avoid this.
Seems easy enough for code first:
public class Entity {
public string UserGUID { get; set; }
[NotMapped]
private UserPrincipal? _user;
[NotMapped]
public UserPrincipal User
{
get
{
if (!_user.HasValue)
_user = UserPrincipal.GetUser(this.UserGUID); // Make this static for easier re-use.
return _user.Value;
}
set
{
UserGUID = value.UserGUID;
_user = value;
}
}
}
[NotMapped] is your friend here (it's in System.ComponentModel.DataAnnotations). You could simplify things by simply returning the function call every time you user get, eg: get { return this.GetUser(); } and remove the _user field, but that would impact performance.
I'm also not sure if you need [NotMapped] on a field (in this case _user), I would try it with and without.
For a list of users:
public class Entity {
public List<string> UserGUIDs { get; set; }
[NotMapped]
private List<UserPrincipal> _users;
[NotMapped]
public List<UserPrincipal> Users
{
get
{
if (_users != null)
_users = UserPrincipal.GetUsers(this.UserGUIDs);
return _users;
}
set
{
this.UserGUIDs = value.Select(u => u.UserGUID).ToList();
_users = value;
}
}
}
Unfortunately there's not really a more elegant way to implement this with EF. Now, it wouldn't be all that difficult to alter EF to do this. EF is open source, fork it and get going if it's a big enough project to be worth it for you.