What are the ways to make property code-generated only - c#

I am trying to create an abstract class which will contain properties and every entity in my solution will inherit this properties.
I do want that abstract class properties to be restricted from any user modification except the changes which are made by code.
What I want to achieve:
public abstract class SystemEntityBase
{
/// <summary>
/// Gets the date when entity was created.
/// </summary>
public DateTime Created { get; } = DateTime.UtcNow;
/// <summary>
/// Gets the date when entity was last active in the system.
/// </summary>
public DateTime LastActive { get; } = DateTime.UtcNow;
}
Because I am using read-only property, the EntityFrameworkCore will not add this fields to my database table when auto-generating the migration scripts.
I am curios what are possible solutions to restrict properties in my case while also create the database columns?

You should be able to use init only properties if you can use c# 9.0.

If I understood problem correctly - you can try using Backing Fields. For your Created property it can look something like that (for some reason using BackingFieldAttribute didn't work for me with my test SQLite and postgres setups, but fluent API did the trick):
public class SomeEntity
{
public DateTime Created => _created
private DateTime _created = DateTime.UtcNow;
}
And in OnModelCreating(ModelBuilder modelBuilder):
modelBuilder.Entity<SomeEntity>()
.Property(b => b.Created)
.HasField("_test");
Also it is possible to remove the need to setup all SomeEntity's by hand via some reflection magic.

Related

Can Entity Framework automatically extend models with datetime fields to keep the changing date

Can you tell Entity Framework to add an extra field for each field of a certain type? For example: Is it possible to generate a ChangedAt datetime field for each boolean field defined in the model, so this
public bool Confirmed { get; set; }
could result in a table with an additional field ConfirmedChangedAt where the value is updated each time the boolean value is changed.
Usually behavior like this should be implemented directly into your business logic and not automatically into the data layer. So I suggest to write something like this:
// entity
public class Order
{
public bool Confirmed { get; set; }
public DateTime? ConfirmedAt { get; set; }
}
// business logic
public class OrderManager
{
.................
public void Confirm( Order order )
{
// changing of entity status
order.Confirmed = true;
order.ConfirmedAt = DateTime.Now;
// storing new entity status
_orderRepository.Update( order );
................
}
}
I think if i understand you correctly, You are expecting the Entity Framework to be able to add columns to the database automatically so that you don't have to add them manually, Well you have 2 cases:
if you are using the database first approach you could achieve this
by using a query that's specific to your needs to add these columns
for you based on the conditions you have.
If you are using the code first approach and you have an existing database you may reverse engineer the database using the Entity Framework Power Tools and you could customize the T4 Templates to generate the entities with the extra properties that you need.
Plain answer no.
But it's depend on way how you interact with EF (code first, model first,database first).
If you using EF 6 and code first approach you can use idea of base Entity class
public class BaseEntity
{
public DateTime ChangedAt {get;set;}
}
public class ConcreteEntity : BaseEntity
{
public string Name {get;set;}
}
Now ConcreteEntity has ChangedAt by inheritance.
If this solution not for you, please explain question with more details.

Entity Framework get user from contex in saveChanges

i have two projects in my solution, UI as mvc and class project for entitiy model code first. I have severall entities in my model but now I need to extend them by new audit fields where I need to save who changed entity.
I added new interface
public interface IAuditable
{
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
DateTime CreatedDate { get; set; }
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
string CreatedBy { get; set; }
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
DateTime UpdatedDate { get; set; }
/// <summary>Gets or sets the name.</summary>
/// <value>The name.</value>
string UpdatedBy { get; set; }
}
and try to extend SaveChanges in this way
foreach (var auditableEntity in ChangeTracker.Entries<IAuditable>())
{
if (auditableEntity.State == EntityState.Added ||
auditableEntity.State == EntityState.Modified)
{
// implementation may change based on the useage scenario, this
// sample is for forma authentication.
string currentUser = ;
// modify updated date and updated by column for
// adds of updates.
auditableEntity.Entity.UpdatedDate = DateTime.Now;
auditableEntity.Entity.UpdatedBy =
// pupulate created date and created by columns for
// newly added record.
if (auditableEntity.State == EntityState.Added)
{
auditableEntity.Entity.CreatedDate = DateTime.Now;
auditableEntity.Entity.CreatedBy = currentUser;
}
else
{
auditableEntity.Property(p => p.CreatedDate).IsModified = false;
auditableEntity.Property(p => p.CreatedBy).IsModified = false;
}
}
but how do I get the userName here ? I can't use any httpContex getUser becuase this is class project. Any ideas?
this is my contex
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDbContext
so I thought to extend ApplicationUser by another field like LogedUserName, and fill it when user is loging, but how do I get this field in my SaveChanges method ?
If you are sure that this class library will be always used in ASP.NET pipeline you actually can access HttpContext.
You need a reference to System.Web in your class library and then:
using System.Web;
[...]
public void SaveChanges()
{
var username = HttpContext.Current.User.Identity.Name;
}
In this case HttpContext is a static class, not a property.
Ofcourse this will fail badly if this class is ever used outside ASP.NET pipeline (for example in WPF application, console app etc). Also it doesn't seem clean to do it this way. But it's probably the fastest way which requires minimal existing code change.
Another way would be to pass either username or whole identity to either class responsible for saving changes or directly to SaveChanges method.
One implementation could look like this:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IDbContext
{
private IPrincipal _currentUser;
public ApplicationDbContext(IPrincipal currentUser)
{
_currentUser = currentUser;
}
}
and then in Controller (if you use context directly in MVC controllers):
using(var db = new ApplicationDbContext(User))
{
[...]
}
where User is controller's property holding current user.

EF Code First - Mapping with "Include" Where Clause

In my case I have a table with the field "IsToDelete" (bit/boolean) where we delete these entries during maintenance time. But regarding the actual aplication we do not process these entries, we ignore them always by using the where condition istodelete == false.
We have several places where we use something like ".Include(x=>x.MyEntities)
I know that some people made similar questions such as:
EF: Include with where clause
However, my question is regarding another type of approach: there are really some places were its not so good idea to "create" a "temp" mapped object (as suggested in EF: Include with where clause).
When we are defining the EF code first mapping (EntityTypeConfiguration).
We have something similar to:
this.Property(t => t.IsToDelete).HasColumnName("IsToDelete");
I am thinking of a solution more of the kind "SQL Views" but without really have a "view".
Can we do it in this "mapping" file or in some other way (ALWAYS apply this filter)?
P.S. Human error of forgetting to add the where condition will no longer occur.
You can make the property setter protected in your Entity Model. Somethink like that:
public class CompanyReference : EntityReference<CompanyReference>
{
#region Public Properties
/// <summary>
/// name
/// </summary>
[StringLength(8)]
[Required]
public string Name
{
get;
protected set;
}
/// <summary>
/// displayname
/// </summary>
[StringLength(45)]
public string DisplayName
{
get;
protected set;
}
#endregion
}

Where to put Created date and Created by in DDD?

I use Entity Framework and want to use DDD principles. However, there are some information regarding the entities that is on the borderline between what is logging/persistence information and what is information about the domain objects.
I my situation these are put in an abstract base class that all entities inherit from:
public abstract class BaseEntity: IBaseEntity
{
/// <summary>
/// The unique identifier
/// </summary>
public int Id { get; set; }
/// <summary>
/// The user that created this instance
/// </summary>
public User CreatedBy { get; set; }
/// <summary>
/// The date and time the object was created
/// </summary>
public DateTime CreatedDate { get; set; }
/// <summary>
/// Which user was the last one to change this object
/// </summary>
public User LastChangedBy { get; set; }
/// <summary>
/// When was the object last changed
/// </summary>
public DateTime LastChangedDate { get; set; }
/// <summary>
/// This is the status of the entity. See EntityStatus documentation for more information.
/// </summary>
public EntityStatus EntityStatus { get; set; }
/// <summary>
/// Sets the default value for a new object
/// </summary>
protected BaseEntity()
{
CreatedDate = DateTime.Now;
EntityStatus = EntityStatus.Active;
LastChangedDate = DateTime.Now;
}
}
Now a Domain Object can't be instantiated without providing the date and time. However, I feel it is the wrong place to put it. I can argue for both really. Maybe it should not be mixed with the domain at all?
Since I'm using EF Code First it makes sense to put it there, or else I would need to create new classes that inherit from the base class in the DAL also, duplicating code and needing to map to both domain objects and MVC models which does seem more messy than the approach above.
The question(s):
Is it Ok to use DateTime.Now in the Domain model at all? Where do you put this kind of information using DDD and EF Code First? Should User to be set in the domain object or require it in the Business Layer?
Update
I think jgauffin har the right answer here - but it is really quite a fundamental change. However, on my search for an alternate solution I almost had it solved with this. I used the ChangeTracker.Entries to find ut if an entity is added or modified and set the fields accordingly. This is done in my UnitOfWork Save() method.
The problem is loading navigation properties, like User (DateTime is set correctly). It might be since the user is a property on the abstract base class the entity inherits from. I also don't like putting strings in there, however it might solve some simple scenarios for someone, so I post the solution here:
public void SaveChanges(User changedBy)
{
foreach (var entry in _context.ChangeTracker.Entries<BaseEntity>())
{
if (entry.State == EntityState.Added)
{
entry.Entity.CreatedDate = DateTime.Now;
entry.Entity.LastChangedDate = DateTime.Now;
entry.Entity.CreatedBy = changedBy;
entry.Entity.LastChangedBy = changedBy;
}
if (entry.State == EntityState.Modified)
{
entry.Entity.CreatedDate = entry.OriginalValues.GetValue<DateTime("CreatedDate");
entry.Entity.CreatedBy = entry.OriginalValues.GetValue<User>("CreatedBy");
entry.Entity.LastChangedDate = DateTime.Now;
entry.Entity.LastChangedBy = changedBy;
}
}
_context.SaveChanges();
}
Is it Ok to use DateTime.Now in the Domain model at all?
Yes.
Where do you put this kind of information using DDD and EF Code First? Should User to be set in the domain object or require it in the Business Layer?
Well. First of all: A DDD model is always in a valid state. That's impossible with public setters. In DDD you work with the models using methods since the methods can make sure that all required information has been specified and is valid.
For instance, if you can mark an item as completed it's likely that the UpdatedAt date should be changed too. If you let the calling code make sure of that it's likely that it will be forgotten somewhere. Instead you should have something like:
public class MyDomainModel
{
public void MarkAsCompleted(User completedBy)
{
if (completedBy == null) throw new ArgumentNullException("completedBy");
State = MyState.Completed;
UpdatedAt = DateTime.Now;
CompletedAt = DateTime.Now;
CompletedBy = completedBy;
}
}
Read my blog post about that approach: http://blog.gauffin.org/2012/06/protect-your-data/
Update
How to make shure that noone changes the "CreatedBy" and "CreatedDate" later on
I usually have two constructors for the models which also fits the DB. one protected one which can be used by my persistance layer and one which requires the mandatory fields. Put the createdby in that constructor and set the createdate in it:
public class YourModel
{
public YourModel(User createdBy)
{
CreatedDate = DateTime.Now;
CreatedBy = createdby;
}
// for persistance
protected YourModel()
{}
}
Then have private setters for those fields.
I get a lot of R# warning "Virtual member call in constructor", I've read about it before and it is not supposed to be a good practice.
That's usually not a problem. Read here: Virtual member call in a constructor
Is it Ok to use DateTime.Now in the Domain model at all?
It isn't terrible, but the problem is that you will end up having to duplicate code and it will more difficult to achieve consistency.
Where do you put this kind of information using DDD and EF Code First?
You are correct to assert that this type of information doesn't belong in your domain. It is typically called an audit log or trail. There are a few ways to implement auditing with EF. Take a look at AuditDbContext - Entity Framework Auditing Context for instance, or just search around for EF auditing implementations. The idea is that before EF persists changes to an entity, it raises an event which you can listen to and assign the required audit values.
Should User to be set in the domain object or require it in the
Business Layer?
It is best to handle this at the infrastructure/repository level with an auditing implementation as stated above. This is the final stop before data is persisted and thus is the perfect place to take care of this.

EntityFramework - Entity proxy error

I am working on a system using Entityframework and have been for over 12monts now, and the project has been going well, up until yesterday, where I have now got a strange error which I have no idea why it occurs.
I am doing nothing different to what I have done before, but once I load the entity in question and try to access any child entities I get the following error:
The entity wrapper stored in the proxy does not reference the same proxy
Can anyone shed any light on what this actually means and what would cause this?
Showing my code doesnt really help.
Here is a simplified version of the code:
var quote = new QuoteHelper().GetById(orderId);
var updatedQuotes = new Provider().GetExportQuotes(quote.DeparturePoint.Id,quote.DestinationPoint);
The error occurs when accessing DeparturePoint and DestinationPoint but Quote loads correctly, and all properties are loaded.
The entity Quote looks a little like this:
public class Quote : BaseQuote, ICloneable
{
public Guid DeparturePointId { get; set; }
public virtual LocationPoint DeparturePoint{ get; set; }
public Guid DestinationPointId { get; set; }
public virtual LocationPoint DestinationPoint{ get; set; }
}
This happened to me too when I tried to implement ICloneable on my entity and cloned it using MemberwiseClone. Worked great when I was using entities that I instantiated myself. However, when I used this to clone an entity that had been loaded using EF, I got this error whenever I tried to add it to a DbSet (or in various other parts).
After some digging, I found that when you clone an EF-loaded entity, you're cloning the proxy class as well. One of the things a proxy class carries around is a reference to the wrapper fo the given entity. Because a shallow copy only copies a reference to the wrapper, you suddenly have two entities that have the same wrapper instance.
At this point, EF thinks you've created or borrowed a different proxy class for your entity which it assumes is for purposes of mischief and blocks you.
Edit
Here's a snippet that I created to work around this problem. Note that this will do a fair job of copying just the EF properties, but it's not perfect. Note that you'll need to modify it if you have private fields that must be copied as well, but you get the idea.
/// <summary>
/// Makes a shallow copy of an entity object. This works much like a MemberwiseClone
/// but directly instantiates a new object and copies only properties that work with
/// EF and don't have the NotMappedAttribute.
/// </summary>
/// <typeparam name="TEntity">The entity type.</typeparam>
/// <param name="source">The source entity.</param>
public static TEntity ShallowCopyEntity<TEntity>(TEntity source) where TEntity : class, new()
{
// Get properties from EF that are read/write and not marked witht he NotMappedAttribute
var sourceProperties = typeof(TEntity)
.GetProperties()
.Where(p => p.CanRead && p.CanWrite &&
p.GetCustomAttributes(typeof(System.ComponentModel.DataAnnotations.NotMappedAttribute), true).Length == 0);
var newObj = new TEntity();
foreach (var property in sourceProperties)
{
// Copy value
property.SetValue(newObj, property.GetValue(source, null), null);
}
return newObj;
}
Above solution may occur such as error "Conflicting changes to the role x of the relationship y have been detected". I achieve that error with using this method;
public virtual TEntity DetachEntity(TEntity entityToDetach)
{
if (entityToDetach != null)
context.Entry(entityToDetach).State = EntityState.Detached;
context.SaveChanges();
return entityToDetach;
}
i hope it'll work for you too.
I solved it this way.
using (var ctx = new MyContext())
{
ctx.Configuration.ProxyCreationEnabled = false;
return ctx.Deferrals.AsNoTracking().Where(r =>
r.DeferralID.Equals(deferralID)).FirstOrDefault();
}

Categories

Resources