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.
Related
I have a model of my content:
class BaseModel {
public virtual string Content{ get; set; }
// ...
}
To display the data only the model above is fine. But I want to add the functionality to edit the content. So I need to add an attribute to the member content - But this should only happen when the autor press an edit button, not in the regular view of the content.
So I created a second model which inherits from the BaseModel so that I can override the member with my attribute:
class EditableBaseModel : BaseModel {
[UIHint("MyEditor"), AllowHtml]
public override string Content{ get; set; }
}
This works fine, but because of the inheritance EF create an additional column discriminator. It contains the type of the class as string. In my case its always BaseModel because I always convert EditableBaseModel to BaseModel before It gets saved to the database like this:
myBbContextInstance.BaseModels.Add(editableBaseModelInstance as EditableBaseModel);
Thus, the discriminator-column is a waste of space and I want to remove it. I found out that this can be done using the NotMapped-attribute. But this will result in the following exception when I try to save the model:
Mapping and metadata information could not be found for EntityType 'EditableBaseModel'.
It seems that the NotMapped-attribute will let EF know that another class exists that inherits from BaseModel, but EF won't get any information about this class. But thats not what I want. I need to tell EF: EditableBaseModel is nothing it should care about because its only to fit my view, and would be never used for the database.
How can I do that? The only way I found out is to convert the EditableBaseModel instance manually to a BaseModel object like this:
public ActionResult Save(EditableBaseModel editableBaseModel) {
var baseModel = new BaseModel() {
Content = editableBaseModel.Content
// ...
};
myDbContextInstance.BaseModels.Add(baseModel);
}
But this seems not a good way to do that because I have multiplice attributes. And it's also not very flexible because when I add something to the BaseModel, I have to add it also here - Which can result in strange errors.
Mixing EF concepts with MVC concepts into a Model may not fits for both. In this case creating new BaseModel and copy the content of EditableBaseModel into BaseModel as you did, is the right way. You can use AutoMapper for mapping data between two models.
class EditableBaseModel
{
[UIHint("MyEditor"), AllowHtml]
public string Content{ get; set; }
}
public ActionResult Save(EditableBaseModel editableBaseModel) {
var baseModel = new BaseModel();
Mapper.Map<EditableBaseModel, BaseModel>(editableBaseModel, baseModel);
myDbContextInstance.BaseModels.Add(baseModel);
.
.
.
}
The bottom line is that, using inheritance in Entity Framework, you can't represent the same record in the database by two different types.
Stated differently, if you use inheritance in any way, EF can materialize any row in the database to one type only. So what you want is never possible, with or without discriminator.
I think the conversion to and from EditableBaseModel is a viable option. Or wrap a BaseModel in a EditableBaseModel, where the latter has delegate properties like
public string Content
{
[UIHint("MyEditor"), AllowHtml]
get { return _baseModel.Content; }
set { _baseModel.Content = value; }
}
This is a common pattern, called Decorator. Note that in that case (or with your conversion) you should not register EditableBaseModel as an entity in the EF model.
Technically, another approach would be possible. You can materialize any object by DbContext.Database.SqlQuery. You could use BaseModel for display purposes only and use EditableBaseModel as the mapped entity class. BaseModels then, could be materialized by
myBbContextInstance.Database.SqlQuery<BaseModel>("SELECT * FROM dbo.BaseModel");
Of course the query can be parametrized to filter the models. The BaseModels will not be tracked by the context, but as you only want to display them, that's not necessary. This is the only way I see to represent (sort of) one record in the database by another type.
While I mention the technical possibility, that doesn't mean I recommend it. But then, even for the editable option I'd prefer using view models. I don't like this tight coupling between data layer and UI.
Have you considered using a constructor in BaseModel which works in the following way:
public BaseModel(EditableBaseModel editableBaseModel) {
this.Content = editableBaseModel.Content
}
and use it like this:
myBbContextInstance.BaseModels.Add(new BaseModel(editableBaseModelInstance));
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.
Background
We have a table, let's call it Files.
We have certain attributes on the row, such as Name, CreatedDate, etc.
We have a blob column with the contents of the file, FileBytes.
So our model looks similar to:
public class FileEntity
{
public string Name { get; set; }
public DateTime CreatedDate { get; set; }
public byte[] FileBytes { get; set; }
// many other fields, most of which we'd like to use
}
Goal
On certain queries, we only care about whether FileBytes is null, not anything about the bytes themselves.
We'd like to be able to query and have a field in our model class populated, say, FileHasBytes that is a bool.
We'd like this field to exist only in our class, so that we can refer to it in the webapp as part of the model.
We'd like to be able to query for this bool value without pulling the full bytes from the field.
Question
How can I, using EF6, define a field on my model class that will be consistently projected, based on another field in the table, without pulling the full contents of that field?
Considered Options / Workarounds
Computed column: we were hoping to avoid this because it seems unnecessary
View: We'd also like to avoid this because it seems unnecessary to go to this for a single column
Projection to a new object: This is doable, but we'd like to be able to map directly without selecting a new object each time, including all of the fields that go with it.
With the current version of EF6 you can't do exactly what you are after.
There are other alternatives, but with all of them you'd have to make compromises on the goals stated above. Such as either using a new projected type with the computed property, or not querying on the computed value and be explicit on the condition instead, etc.
However, using something like DelegateDecompiler it might be possible to have the model and query just as you are expecting.
Something along the lines of:
[Computed]
public bool HasFileBytes
{
get { return FileBytes != null; }
}
And in your query you'd use the .Decompile() call to get that translated:
var query = ctx.Files
.Where(x => x.HasFileBytes)
.Decompile();
Using Linq.Translations could be another similar alternative.
source: http://www.daveaglick.com/posts/computed-properties-and-entity-framework
This is not ideal but I think you can add a static property that returns QueryExpression like
public static Expression<Func<FileEntity,bool>> FileHasBytes
{
get { return ((c)=> c.FileBytes != null && SqlFunctions.DataLength(c.FileBytes)>0)
}
I have not tried this code, so take this with grain of salt, so try and test it thoroughly.
I have used some thing like this using Dynamic.linq some time ago, but not tried it lately
dbContext.FileEntities.Where("FileHasBytes == true"),
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.
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 = "";
}
}