EF Code First - Mapping with "Include" Where Clause - c#

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
}

Related

Entity Framework Core: Class entity inheritance to add navigation property - Avoid discriminator column?

Using Entity Framework Core 3.1 I created a new entity class that inherits from an existing entity in my data model. These classes exist in different solutions so that's why I have to make this change through inheritance and not by simply modifying the original entity.
The only intent of this inheritance is to add a new navigation property for a new many to many relationship that I created.
By default, Entity Framework is creating a new Discriminator column which makes sense for most cases but not in this one. Please notice that I am not adding new fields through my child class, the schema remains unchanged. I just want to have a navigation property for that new relationship which is represented by a join table.
/// Original entity
public class LineItemEntity
{
public string Id { get; set; }
public string PriceId { get; set; }
public string Currency { get; set; }
}
/// My entity adding the new navigation property
public class CustomOrderLineItemEntity : LineItemEntity
{
/// <summary>
/// Order item proofs related to the order line item
/// </summary>
public virtual List<OrderItemProofEntity> OrderItemProofs { get; set; }
= new List<OrderItemProofEntity>();
}
The issue I have is that when I use the repository that references the child (CustomOrderLineItemEntity) all of my queries are going to the database with a where clause WHERE [o].[Discriminator] = N'CustomOrderLineItemEntity' and I am suspecting this is making all my queries fail as there are no entities with that value set in the Discriminator, and most probably they will never be as the inserts will remain to happen with the Parent entity (LineItemEntity) not the child.
Is there any way to force EF to don't create the discriminator for a case like the one I am describing or is there a better way to approach what I need to do?

What are the ways to make property code-generated only

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.

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.

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.

Nhibernate cache problem while using SQL Server triggers

I am using Nhibernate with Fluent, to persist a SQL Server 2008 express database in a business application.
I have a class named Receipt which contains a list with many objects named ReceiptItems.
The user can create a receipt, add Receiptitems to it, and edit it as long as its not marked Finished.
This part works well and saves to the db correctly.
Now for the problem:
I also have a trigger on the sql table Receipt, that fires if the inserted.Finished is true.
The trigger fetches new prices from the "supplier table", and updates the prices for all ReceiptItems,
in the ReceiptItems table.
When calling
session.SaveorUpdate(value)
and then
transaction.Commit()
the latter causes the exception:
StaleObjectStateException
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) in ReceiptItems
Removing the trigger ofcourse fixes the problem, but i need it to update the prices. Is there any way for nhibernate to ignore the
error, and refresh its cache after the trigger fires?
straightforward class example definitions with Fluent mappings:
public class Receipt
{
public Receipt() { }
/// <summary>Identificator/// </summary>
public virtual int Id { get; private set; }
/// <summary> if finished true, cant edit data/// </summary>
public virtual Boolean Finished { get; set; }
/// <summary>Items of this Receipt/// </summary>
public virtual IList<ReceiptItems> Items{ get; set; }
}
/// <summary>Mapping for NHibernate Fluent/// </summary>
public class ProdajaMap : ClassMap<Prodaja>
{
public ReceiptMap()
{
Table("Receipt");
OptimisticLock.All();
DynamicUpdate();
Id(x => x.Id);
Map(x => x.Finished);
HasMany<ReceiptItems>(x => x.Items).AsBag().KeyColumn("Receipt_ID");
}
}
public class ReceiptItem
{
public ReceiptItem() { }
public virtual int Id { get; private set; }
/// <summary>Id of the Receipt/// </summary>
public virtual int Receipt_ID{ get; set; }
/// <summary>Supplier price/// </summary>
public virtual decimal Price{ get; set; }
/// <summary>Supplier discount/// </summary>
public virtual decimal Discount { get; set; }
}
/// <summary>Mapping for NHibernate Fluent/// </summary>
public class ReceiptItemMap : ClassMap<ReceiptItem>
{
public ReceiptItemMap()
{
Table("ReceiptItems");
OptimisticLock.All();
DynamicUpdate();
Id(x => x.Id);
Map(x => x.Receipt_ID).Column("Receipt_ID");
Map(x => x.Price);
Map(x => x.Discount );
}
}
Thank you very much !
UPDATE:
I've found a nhibernate property, which does exactly what i need, as the Price and Discount values have to be generated by the trigger:
5.6. Generated Properties Generated properties are properties which have
their values generated by the
database. Typically, Hibernate
applications needed to refresh objects
which contain any properties for which
the database was generating values.
Marking properties as generated,
however, lets the application delegate
this responsibility to Hibernate.
Essentially, whenever Hibernate issues
an SQL INSERT or UPDATE for an entity
which has defined generated
properties, it immediately issues a
select afterwards to retrieve the
generated values.
Properties marked as generated must
additionally be non-insertable and
non-updateable.
As im new at this.., does the last sentance mean, that i can't insert or update the values with nhibernate?
Use SET NOCOUNT ON in the trigger to suppress (xx rows affected) "dummy" result sets from trigger processing.
Example SO question: TooManyRowsAffectedException with encrypted triggers

Categories

Resources