How do I override the get; set; properties of a field? - c#

I'm working with a database that due to reason out of my control - I cannot modify the schema.
This database has a field "CertificateId" that is non-nullable, however, the field is still considered to be optional. When I load this field into my model (I'm using the DB first approach), it is of course tagged as being non-nullable, as you can see in the designer.cs
[EdmScalarPropertyAttribute(EntityKeyProperty = false, IsNullable = false)]
[DataMemberAttribute()]
public global::System.String CertificateId
{
get
{
return _CertificateId;
}
set
{
OnCertificateIdChanging(value);
ReportPropertyChanging("CertificateId");
_CertificateId = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("CertificateId");
OnCertificateIdChanged();
}
}
Since this field is optional, it gets passed null sometimes, which obviously fails validation.
Is there any way of overloading these properties that are autogenerated? I would like to be able to check if the value passed into the set property is null, and if it is, set it to an empty string before it goes into validation.
Or, is it possible to override the metadata for this property and have IsNullable set to true?

First of all if the field is considered as not nullable you mustn't set its value to null. Once you manually assign null in your code it's your bug.
Another problem which have to be solved is default value (null) if you don't assign the value. This is well discussed in this question - I like the way with initializing the field with constructor.
If for any reason previous two options are not what you want you have a last choice of modifying the value in overriden SaveChanges. Something like:
public override int SaveChanges(SaveOptions options)
{
var data = context.ObjectStateManager
.GetObjectStateEntries(EntityState.Added | EntityState.Modified)
.Where(e => !e.IsRelationship)
.Select(e => e.Entity)
.OfType(MyEntity);
foreach(var entity in data)
{
if (entity.CertificateId == null)
{
entity.CertificateId = String.Empty;
}
}
return base.SaveChanges(options);
}

If You created your model classes with a designer, then you can manually set this property to be nullable in your model.

If your field is optional, why not make the database column nullable, instead of putting in empty string?
You could use poco's, or you could update the existing entity framework template file (T4 templates), so that you can add your null check within the template, and when the entity framework classes are generated is uses your template. I'll try and find you some links I've used in the past.
Here's information on using POCO's
http://msdn.microsoft.com/en-us/library/dd456853.aspx
Another way to handle this is to create a stored procedure, add that to your model, and handle your logic to make nulls empty strings within the sp.

Thanks guys, I simply used the constructor in a partial class to set my default:
public partial class Emp_Certificate
{
//Constructor
public Emp_Certificate()
{
this.CertificateID = "";
}
}

Related

Attribute for "Property not updateble"

I have an application (webservice), where numerous objects are inserted into a database or updated (with Entity Framework), but never read. So I even don't have read-access to this database.
Now a new requirement wants some properties to be inserted but never to be updated. I save all objects like this:
Type t = obj.GetType();
db.Set<TT>().AddOrUpdate(MapObject<TT>(obj)); //Maps to database entity and saves object
Now the question is, if there is an attribute I can tell a property to be inserted, but ignored while updating? In best case something like this:
[InsertOnly]
public string SomeText { get; set; }
In your unit of work save routine, check ChangeTracker and remove those records or properties that are marked as update. Something like this:
var modifiedItems = _dbContext.ChangeTracker.Entries()
.Where(x => x.State == EntityState.Modified)
.ToList();
EntityState has following types:
Detached
Unchanged
Added
Deleted
Modified
You can add a custom attribute.
public class InsertOnly : Attribute
{}
If we consider
on your EF object, you add the custom property as such:
public class SomeEFClass {
[InsertOnly]
public int Id {get;set;}
}
Then change
db.Set<TT>().AddOrUpdate(MapObject<TT>(obj)); //Maps to database entity and
To include something like (This is pseudo code, don't expect it to run)
foreach(Property info pi in t.GetProperties())
{
if(pi.GetCustomAttribute<InsertOnly>() == null)
{
//You can safely update this property
}
else
{
//This property is only for inserting
}
}
I am uncertain if you can add the custom attribute via partial class overloading of a property? That might be worth a shot?

Entity Framework to read a column but prevent it being updated

Given a database table with a column that contains historic data but that is no longer populated, is there a way in Entity Framework to read the column but prevent it being updated when using the same model object?
For example I have an object
public class MyObject
{
public string CurrentDataColumnName { get; set; }
public string HistoricDataColumnName { get; set; }
}
From the documentation I don’t believe I can do either of the following, because this will stop EF reading the data as well as persisting it.
(1) Decorate the HistoricDataColumnName property with the following attribute
[NotMapped]
(2) Add the following to my EntityTypeConfiguration for MyObject
Ignore(x => x.HistoricDataColumnName)
You can mark the column as computed to prevent Entity Framework from updating / inserting into that column.
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string HistoricDataColumnName { get; set; }
DatabaseGenerated
An important database features is the ability to have computed
properties. If you're mapping your Code First classes to tables that
contain computed columns, you don't want Entity Framework to try to
update those columns. But you do want EF to return those values from
the database after you've inserted or updated data. You can use the
DatabaseGenerated annotation to flag those properties in your class
along with the Computed enum. Other enums are None and Identity.
You can simply use IsModified to check whether a specific entity property was modified or not and by this way you can still Read,Insert and Delete data:
var item = context.MyObjects.Find(id);
item.CurrentDataColumnName = "ChangedCurrentDataColumnName";
item.HistoricDataColumnName = "ChangedHistoricDataColumnName";
context.Entry(item).Property(c => c.HistoricDataColumnName).IsModified = false;
context.SaveChanges();
By using IsModified = false you are excluding the HistoricDataColumnName property from updating, so the HistoricDataColumnName column will not be updated in the database but other properties will be updated.
Setting this value to false for a modified property will revert the change by setting the current value to the original value. If the result is that no properties of the entity are marked as modified, then the entity will be marked as Unchanged. Setting this value to false for properties of Added, Unchanged, or Deleted entities is a no-op.
Check the following answer as a supplementary explanation. It might be helpful also:
https://stackoverflow.com/a/13503683/2946329
Codewise you can set the setter simply to protected. EF useses reflection to materialize your model. I think the now hidden setter also shows to every other programmer, that the field should not be modified any longer.
Also add an [Obsolete]-attribute with further information, why the property can't be set from the public anymore.
Since you say 'at the EF level or lower' a possible solution is to use a trigger to either raise an error if an attempt is made to change the column, or allow the update but ignore the change on the column of interest.
Option 1 - raise an error
CREATE TRIGGER MyTable_UpdateTriggerPreventChange
ON dbo.Table1
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
if update(HistoricDataColumnName)
begin
raiserror (50001, 16, 10)
end
END
Option 2 - ignore the change
CREATE TRIGGER MyTable_UpdateTriggerIgnore
ON dbo.Table1
INSTEAD OF UPDATE
AS
BEGIN
SET NOCOUNT ON;
update dbo.Table1 set HistoricDataColumnName=inserted.HistoricDataColumnName
from inserted
where inserted.Id = dbo.Table1.Id
END
You could of course do something similar for inserts if required.
Alternatively to raiserror use 'throw'
ALTER TRIGGER MyTable_UpdateTriggerPreventChange
ON dbo.Table1
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
if update(HistoricDataColumnName)
begin
throw 50002, 'You can''t change the historic data', 1
end
END
either way you'll get an exception thrown. This is using LinqPad
For just on column this is overkill, but in general you can override SaveChanges in DbContext to have more control on the changes.
In your model:
public override int SaveChanges()
{
var modifiedEntries = base.ChangeTracker.Entries<MyObject>()
.Where(e => e.State == EntityState.Modified).ToList();
foreach (var entry in modifiedEntries)
{
// Overwriting with the same value doesn't count as change.
entry.CurrentValues["HistoricDataColumnName"] = entry.OriginalValues["HistoricDataColumnName"];
}
return base.SaveChanges();
}
But you could also undo all modifications by changing the state from modified to unchanged.
-- UPDATE --
There is one thing that worries me. As soon as a developer has the credentials to access the database you cannot prevent them from doing things you don't want. They could create their own model or query the database directly.
So I think the most important thing to do is to set the field to readonly in the database for the client. But you may not be able to lock one column.
Even if this is not an issue, I think (for design) it is better to move all historical data to other tables. Making it easy to grant readonly access only. You can map these tables 1:1. With Entity Framework you can still access the historical information quite easy.
But in that case you won't have the problem you have now and will give you other options to prevent others from changing the historical information.
internal access modifier
You could change the setter to internal
public class MyObject
{
public string CurrentDataColumnName { get; internal set; }
public string HistoricDataColumnName { get; internal set; }
}
This doesn't impose as much limitations as the other options, but depending on your requirements, this can be quite useful.
protected access modifier
This would probably be the most common usage of making a property in EF "read-only". Which essentially only allows the constructor to access the setter (and other methods within the class, and classes derived from the class).
public class MyObject
{
public string CurrentDataColumnName { get; protected set; }
public string HistoricDataColumnName { get; protected set; }
}
I think protected is what you're looking for.
protected internal access modifier
You can also combine the two like this, to make it protected or internal
public class MyObject
{
public string CurrentDataColumnName { get; protected internal set; }
public string HistoricDataColumnName { get; protected internal set; }
}
Access Modifier Refresher Course
A internal member is accessible only within the same assembly
A protected member is accessible within its class and by derived class instances.
A protected internal member can be accessed from the current assembly or from types that are derived from the containing class.
The question is about EF 6, but this is easily doable in EF Core with the Metadata.IsStoreGeneratedAlways property. Thanks to ajcvickers on the EF Core repo for the answer.
modelBuilder
.Entity<Foo>()
.Property(e => e.Bar)
.ValueGeneratedOnAddOrUpdate()
.Metadata.IsStoreGeneratedAlways = true;
Why do this in EF in the first place? Why not simply ensure that any login being used to access the database either has the rights for performing UPDATE/INSERT/DELETE revoked or even go to the extreme of setting the database to READ_ONLY in the Database options?
It seems to me that any attempt to prevent updates via EF is doomed as you can always circumvent that and, for example, just execute SQL code directly against the EF connection.
As for me, it's simple solution - make property setters as private:
public class MyObject
{
public string CurrentDataColumnName { get; private set; }
public string HistoricDataColumnName { get; private set; }
}
EF will materialize objects from database without any problem, but yout won't have any way to change value int these properties.

Entity framework map placeholder value to null

In a legacy database, NULL values are sometimes stored as the string NULL and/or "x".
How can I tell EF that I want it to automatically convert those "placeholder values" to NULL?
Update:
I want to avoid ambiguity / accidental complexity in my model by having the same property twice. (If people can get/set LegacyColumn, they will and abstraction is broken)
You can create a new property based on the existing property that is not mapped to the database and that performs the desired conversion. Assuming the column is named Foo you can use this code:
[Column("Foo")]
public String FooWithWeirdNullValues { get; set; }
[NotMapped]
public String Foo {
get {
return FooWithWeirdNullValues == "NULL" || FooWithWeirdNullValues == "x"
? null : FooWithWeirdNullValues;
}
set { FooWithWeirdNullValues = value; }
}
In your code you then use the unmapped property to access the value of the Foo column.
You might want to pick another name for FooWithWeirdNullValues e.g. LegacyFoo or whatever. You can pick any name as long as you use the Column attribute to map it to the correct column in the database.
If desired you can make FooWithWeirdNullValues private to not pollute your model. This requires a few changes to the DbContext as described in an answer on Stack Overflow on how to map private properties using code first.

Prevent EF 5 from generating a property

I'm using EF5 database first with partial classes. There's a property in my partial class which contains n object which is stored as a column in my database containing XML data. I want to handle the serialization/deserialization of this object when the EF tries to read/write it with a custom getter/setter.
Is it possible to expose the column in my partial class and map it using the EF, without auto-generating a property for it?
ie:
public SomeObject BigComplexObject { get; set; } // forms etc in my app use this
public string BigComplexObjectString // when the EF tries to read/write the column, my custom getter/setter kicks in
{
get { return this.BigComplexObject.ToXmlString(); }
set { this.BigComplexObject = new BigComplexObject(value); }
}
At present, the EF is auto-generating a member for the column so I'm left with two.
Try to change the logic. Leave EF generated property that will be populated with XML string from the database:
public string BigComplexObjectString { get; set; }
Then do the following:
[NotMapped]
public SomeObject BigComplexObject
{
get { return new SomeObject(this.BigComplexObjectString); }
set { this.BigComplexObjectString = value.ToXmlString(); }
}
Don't forget to add [NotMapped] to instruct EF to ignore this property.
Well, we use a little trick for a quite similar case...
We use the property panel (in the edmx file) of our... properties and add something in the "documentation" (summary or long description) line (probably not the best place, but anyway). This can be access by your T4 file.
So you could write something like "useXml" in the property panel, then modify your tt to generate the desired code when (example to get the info in the .tt file)
if (edmProperty.Documentation != null && edmProperty.Documentation.Summary = "useXml")
//generate something special
It would be great to have a better place for "cusom infos" in the edmx, but we didn't find anything better for instant.

Entity Framework Code First: Computed property of type Type causes ArgumentNullException on Update-Database

As far as I understood, property of type Type cannot be directly stored in a database. Thus, I decided to create a wrapper - I want Entity Framework to store type name and create a property to return desired type:
[Table("DescriptiveCategory")]
public class DescriptiveCategory : Category {
public string RelatedTypeName { get; set; }
public Type RelatedType {
get {
return Type.GetType(RelatedTypeName);
}
set {
RelatedTypeName = value.FullName;
}
}
}
This, unfortunately, does not work: if I try to update database, I get
Value cannot be null.
Parameter name: extent
If I comment RelatedType property, everything is fine. But I don't want Entity Framework to anything with this property - it serves only my needs. I could probably create get and set methods instead but I don't understand why this approach (with computed properties) doesn't work.
Use NotMappedAttribute:
Denotes that a property or class should be excluded from database mapping.

Categories

Resources