Serialization approach in relationship - codefluent

We are using codefluent entities for the BOM, webapi for the controllers and angularjs Framework on the client side.
We are facing a problem when storing reference of an object in the parent object. Anytime, reference would be nulled by generated code.
Given two entities with a Relationship EntA[EntAId, prop1, EntB] and EntB[EntBId, prop1, prop2]
I end up with two classes:
class EntA{
EntAId
prop1
EntB
EntBEntBId
}
and
class EntB{
EntBId
prop1
prop2
}
CodeFluent has generated following code:
[System.Xml.Serialization.XmlElementAttribute(IsNullable=false)]
[System.ComponentModel.DataObjectFieldAttribute(true)]
public System.Guid EntBEntBId
{
get
{
if (((this._EntBEntBId.Equals(CodeFluentPersistence.DefaultGuidValue) == true)
&& (this._entB != null)))
{
this._EntBEntBId = this._entB.EntBId;
}
return this._EntBEntBId;
}
set
{
if ((System.Collections.Generic.EqualityComparer<System.Guid>.Default.Equals(value, this.EntBEntBId) == true))
{
return;
}
this._entB = null;
this._EntBEntBId = value;
this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntB"));
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntBEntBId"));
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public Namespace.EntB.EntB EntB
{
get
{
if ((this._entB == null))
{
this._entB = Namespace.EntB.EntB.Load(this._EntBEntBId);
}
return this._entB;
}
set
{
this._EntBEntBId = CodeFluentPersistence.DefaultGuidValue;
this._entB = value;
this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntB"));
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntBEntBId"));
}
}
On client side (using Angular) I want to register objectA.EntBEntBId without sending the whole EntB object. Thus I would consider this snippet below to nullify EntB.
if(objectA.EntB)
objectA.EntB = null
This end up sending correct Stream to server (without the whole B object being serialized).
When HTTP PUT call is triggered, webapi would valuate classes first going through get/set methods. Property EntBEntBId would be valuated correctly, but then setter of EntB property would keep erasing previous value (as EntB is currently null).
Is there any way to avoid such behavior ?
Thanks in advance for your answer;

I might be answering my own question, but when using delete objectA.entB instead of objectA.entB = null Stream would not contain entB property, thus not going through setter of it.

Related

Update method throws an exception, because the entity is already tracked

I have a problem with EF Core 5 that is really getting me down.
FYI, LazyLoadingProxies are used (something else that just gives me a headache, but well, different topic).
Information for the code below:
Service: A service per entity, contains all CRUD operations into the database and other methods if needed.
Workflow: Uses multiple services at once to perform certain operations (e.g. create product -> create product folder -> save product).
Problem:
I have an entity "Product" which contains the following update method which is used to update the properties of the entity with those of another object:
public override void Update(Product source)
{
// Properties
AnnualPrice = source.AnnualPrice;
...
// Relations
var sourceRelatedProductIds = source.RelatedWithProductIds.Where(x => x != Id);
if (sourceRelatedProductIds.Count() != 0)
{
RelatedWithProducts.Clear();
foreach (var relatedWithProduct in ctx.Set<Product>().Where(x => source.RelatedWithProductIds.Contains(x.Id)).AsNoTracking())
{
RelatedWithProducts.Add(relatedWithProduct);
}
}
var oldShortDescriptions = ShortDescriptions.ToList(); <--- EXCEPTION
ShortDescriptions.Clear();
foreach (var shortDescription in source.ShortDescriptions)
{
shortDescription.Id = oldShortDescriptions.FirstOrDefault(x => x.Culture == shortDescription.Culture)?.Id ?? 0;
ShortDescriptions.Add(shortDescription);
}
...
}
In the line with the arrow and "Exception", I get the following exception:
System.InvalidOperationException: 'The instance of entity type 'Product' cannot be tracked because another instance with the key value '{Id: 1}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.'
In itself, I understand what the exception is trying to tell me. My problem is that I can't find the reason for it anywhere. Because as far as I can tell, the product with ID 1 can't be tracked yet.
Of course, the problem is not in the update method, but before it, so here is the rest of the code.
ProductController.Update:
[HttpPut("update")]
public IActionResult Update(C.Product[] products)
{
if (!ModelState.IsValid)
{
return UnprocessableEntity(ModelState.Values.SelectMany(x => x.Errors));
}
var dbProducts = products.Select(ToDatabase).ToArray(); <--- Just converts the given client model into a Database model
var result = productWorkflow.Update(dbProducts); <--- Calls a workflow class, NOT the update method of the entity
return CoreToActionResultConverter.ToActionResult<Db.Product>(result);
}
ProductWorkflow.Update:
public ResultBase Update(params Product[] products)
{
var result = productService.AddOrUpdate(products); <--- This calls the Service CRUD AddOrUpdate method
if (result is not ServiceResult<Product>)
{
return result;
}
return new ServiceResult<Product>(ResultType.AddedOrUpdated);
}
ProductService.AddOrUpdate:
public virtual ResultBase AddOrUpdate(IEnumerable<TEntity> entities)
{
var currentEntities = new List<TEntity>();
foreach (var entity in entities)
{
var currentEntity = Get(entity.Id); <--- This line is the only one where I could imagine that it is already tracked here. The problem is only that it does not work ONLY with the workflow. If I call my AddOrUpdate method from the controller, which directly calls THIS method, it works (although this line is just executed the same way).
if (currentEntity == null)
{
currentEntity = Ctx.CreateProxy<TEntity>();
Ctx.Attach(currentEntity);
}
if (currentEntity != entity)
{
currentEntity.Update(entity);
}
currentEntities.Add(currentEntity);
}
Ctx.AddRange(currentEntities.Where(x => x.Id == 0));
Ctx.UpdateRange(currentEntities.Where(x => x.Id != 0));
try
{
Ctx.SaveChanges();
}
catch (DbUpdateException ex)
{
// Commented out the error handling to remove unnecessary things for the post
}
return new ServiceResult<TEntity>(ServiceResult.ResultType.AddedOrUpdated, currentEntities);
}
I found the problem and it was not on the line where the exception was thrown, but before.
In my Product.Update() method (the first code snippet), I get the Related Products by ID and add them to the list (Simply a Many to Many relationship, from Product <--> Product). When calling Update, I specified ID 1 in the RelatedProductIds, however the entity itself also has ID 1, so it references itself. I have now just fixed that by omitting the ID, if the same as the object itself.
This still doesn't explain why it works with a breakpoint, because it's still tracked in that case (or not tracked, since I'm using AsNoTracking(), but good).

MVC5 : Attribute Routing Precedence Among Controllers

I am using the Attribute Routing from MVC5 in my controllers.
Question:
Is there a way to control attribute routing precedence among controllers?
Consider the following
[Route("home/{action=index}/{username?}")]
public class HomeController : Controller
{
[Route("home/index/{username?}", Order = 1)]
[Route("home/{username?}", Order = 2)]
[Route("{username?}", Order = 3)]
public ActionResult Index()
{
// ... bunch of stuff
}
}
Base on the code above, HomeController.Index() action method should be invoked using the following requests:
domain/
domain/{username}
domain/home/
domain/home/{username}
domain/home/index/
domain/home/index/{username}
Second Controller:
[Authorize(Roles = "Member")]
[Route("profile/{action=index}")]
public class ProfileController : Controller
{
[Route("profile")]
public ActionResult Index()
{
}
}
The ProfileController.Index() should be invoked using the following request.
domain/profile
domain/profile/index
The problem
From the examples, if I send domain/profile in the url, an ambiguity exception is thrown. It seems that there is an ambiguity between domain/{username} and domain/profile.
Now, if I used convention-based routing, this would have worked (first match wins). But can it be done in MVC5 Attribute Routing? because I found that a third party library supports precedence among controllers
https://github.com/mccalltd/AttributeRouting/wiki/Controlling-Route-Precedence
routes.MapAttributeRoutes(config =>
{
config.AddRoutesFromController<ProfileController>();
config.AddRoutesFromController<HomeController>();
});
No, it is not possible in ASP.Net MVC 5.2.3 to prioritise controller routes over each other. If multiple match, then the order of the actions is ignored and an exception is thrown.
I have verified this by downloading the source from https://aspnetwebstack.codeplex.com/SourceControl/latest and checking the function GetControllerTypeFromDirectRoute (below). None of the calls made out of this function do anything to prioritise the routes, they are just found and reported back. As you can see, GetControllerTypeFromDirectRoute just throws on a multiple match.
Not great at all, but hopefully this will save someone else some time.
I put a manually mapped route in to avoid this issue.
private static Type GetControllerTypeFromDirectRoute(RouteData routeData)
{
Contract.Assert(routeData != null);
var matchingRouteDatas = routeData.GetDirectRouteMatches();
List<Type> controllerTypes = new List<Type>();
foreach (var directRouteData in matchingRouteDatas)
{
if (directRouteData != null)
{
Type controllerType = directRouteData.GetTargetControllerType();
if (controllerType == null)
{
// We don't expect this to happen, but it could happen if some code messes with the
// route data tokens and removes the key we're looking for.
throw new InvalidOperationException(MvcResources.DirectRoute_MissingControllerType);
}
if (!controllerTypes.Contains(controllerType))
{
controllerTypes.Add(controllerType);
}
}
}
// We only want to handle the case where all matched direct routes refer to the same controller.
// Handling the multiple-controllers case would put attribute routing down a totally different
// path than traditional routing.
if (controllerTypes.Count == 0)
{
return null;
}
else if (controllerTypes.Count == 1)
{
return controllerTypes[0];
}
else
{
throw CreateDirectRouteAmbiguousControllerException(controllerTypes);
}
}

How do I get EF to persist empty strings as NULL?

In my domain, there's no important distinction between NULL and an empty string. How do I get EF to ignore the difference between the two and always persist an empty string as NULL?
Empty string is not default value for string property so it means your code is setting empty strings somewhere. In such case it is your responsibility to handle it.
If you are using code first with POCOs you can use custom setter:
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
if (value == String.Empty)
{
_myProperty = null;
}
else
{
_myProperty = value;
}
}
}
Here is a function I placed in my DbContext subclass that replaces empty or whitespace strings with null.
I still didn't optimize it so any performance hints will be very appreciated.
private const string StringType = "String";
private const EntityState SavingState = EntityState.Added | EntityState.Modified;
public override int SaveChanges()
{
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var savingEntries =
objectContext.ObjectStateManager.GetObjectStateEntries(SavingState);
foreach (var entry in savingEntries)
{
var curValues = entry.CurrentValues;
var fieldMetadata = curValues.DataRecordInfo.FieldMetadata;
var stringFields = fieldMetadata.Where(f =>
f.FieldType.TypeUsage.EdmType.Name == StringType);
foreach (var stringField in stringFields)
{
var ordinal = stringField.Ordinal;
var curValue = curValues[ordinal] as string;
if (curValue != null && curValue.All(char.IsWhiteSpace))
curValues.SetValue(ordinal, null);
}
}
return base.SaveChanges();
}
Optimization considerations:
Identify a string type property by different way other than string comparison I tried to look-up some enumeration of the built-in types but didn't find
Cache string fields for types (maybe is unnecessary, will have to decompile and see what the original impl does
Order result by entity type, backup iterated entity type, if next iterated entity is same type, use previous metadata, again, if the metadata is anyway there, performance is cheaper the way it is
Limit string length for whitespace check - i.e. if a string length > x, skip checking whether its a whitespace string or not
I'm using Silverlight and the TextBoxes in the UI set all the string properties to empty strings.
I tried setting:
<TextBox
Text="{Binding MyStringProperty,
Mode=TwoWay,
ValidatesOnDataErrors=True,
TargetNullValue=''}"/>
But it didn't help much.
That's not Entity Framework's job.
You should do it in your repository, or in the database with triggers.
Or do it at the start (e.g when the data comes in, UI, external source, etc)

Entity Framework - Auditing activity

My database has a 'LastModifiedUser' column on every table in which I intend to collect the logged in user from an application who makes a change. I am not talking about the database user so essentially this is just a string on each entity. I would like to find a way to default this for each entity so that other developers don't have to remember to assign it any time they instantiate the entity.
So something like this would occur:
using (EntityContext ctx = new EntityContext())
{
MyEntity foo = new MyEntity();
// Trying to avoid having the following line every time
// a new entity is created/added.
foo.LastModifiedUser = Lookupuser();
ctx.Foos.Addobject(foo);
ctx.SaveChanges();
}
There is a perfect way to accomplish this in EF 4.0 by leveraging ObjectStateManager
First, you need to create a partial class for your ObjectContext and subscribe to
ObjectContext.SavingChanges Event. The best place to subscribe to this event is inside the OnContextCreated Method. This method is called by the context object’s constructor and the constructor overloads which is a partial method with no implementation:
partial void OnContextCreated() {
this.SavingChanges += Context_SavingChanges;
}
Now the actual code that will do the job:
void Context_SavingChanges(object sender, EventArgs e) {
IEnumerable<ObjectStateEntry> objectStateEntries =
from ose
in this.ObjectStateManager.GetObjectStateEntries(EntityState.Added
| EntityState.Modified)
where ose.Entity != null
select ose;
foreach (ObjectStateEntry entry in objectStateEntries) {
ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues
.DataRecordInfo.FieldMetadata;
FieldMetadata modifiedField = fieldsMetaData
.Where(f => f.FieldType.Name == "LastModifiedUser").FirstOrDefault();
if (modifiedField.FieldType != null) {
string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.String.ToString()) {
entry.CurrentValues.SetString(modifiedField.Ordinal, Lookupuser());
}
}
}
}
Code Explanation:
This code locates any Added or Modified entries that have a LastModifiedUser property and then updates that property with the value coming from your custom Lookupuser() method.
In the foreach block, the query basically drills into the CurrentValues of each entry. Then, using the Where method, it looks at the names of each FieldMetaData item for that entry, picking up only those whose Name is LastModifiedUser. Next, the if statement verifies that the LastModifiedUser property is a String field; then it updates the field's value.
Another way to hook up this method (instead of subscribing to SavingChanges event) is by overriding the ObjectContext.SaveChanges Method.
By the way, the above code belongs to Julie Lerman from her Programming Entity Framework book.
EDIT for Self Tracking POCO Implementation:
If you have self tracking POCOs then what I would do is that I first change the T4 template to call the OnContextCreated() method. If you look at your ObjectContext.tt file, there is an Initialize() method that is called by all constructors, therefore a good candidate to call our OnContextCreated() method, so all we need to do is to change ObjectContext.tt file like this:
private void Initialize()
{
// Creating proxies requires the use of the ProxyDataContractResolver and
// may allow lazy loading which can expand the loaded graph during serialization.
ContextOptions.ProxyCreationEnabled = false;
ObjectMaterialized += new ObjectMaterializedEventHandler(HandleObjectMaterialized);
// We call our custom method here:
OnContextCreated();
}
And this will cause our OnContextCreated() to be called upon creation of the Context.
Now if you put your POCOs behind the service boundary, then it means that the ModifiedUserName must come with the rest of data from your WCF service consumer. You can either expose this
LastModifiedUser property to them to update or if it stores in another property and you wish to update LastModifiedUser from that property, then you can modify the 2nd code as follows:
foreach (ObjectStateEntry entry in objectStateEntries) {
ReadOnlyCollection fieldsMetaData = entry.CurrentValues
.DataRecordInfo.FieldMetadata;
FieldMetadata sourceField = fieldsMetaData
.Where(f => f.FieldType.Name == "YourPropertyName").FirstOrDefault();
FieldMetadata modifiedField = fieldsMetaData
.Where(f => f.FieldType.Name == "LastModifiedUser").FirstOrDefault();
if (modifiedField.FieldType != null) {
string fieldTypeName = modifiedField.FieldType.TypeUsage.EdmType.Name;
if (fieldTypeName == PrimitiveTypeKind.String.ToString()) {
entry.CurrentValues.SetString(modifiedField.Ordinal,
entry.CurrentValues[sourceField.Ordinal].ToString());
}
}
}
Hope this helps.
There is a nuget package for this now : https://www.nuget.org/packages/TrackerEnabledDbContext
Github: https://github.com/bilal-fazlani/tracker-enabled-dbcontext

EFPocoAdapter -- PopulatePocoEntity has null PocoEntity

I'm trying EF with the EFPocoAdapter for the first time. I have a relatively simple TPH scenario with one table and two types, each inheriting from an abstract base class.
My model validates through EdmGen, and my PocoAdapter.cs and xxxEntities.cs files generate fine as well. (well, actually, there are some namespace problems that I'm currently tweaking by hand until we figure out where to go next.)
When I run a simple test to retrieve data:
using (CINFulfillmentEntities context = new CINFulfillmentEntities())
{
// use context
var alerts = from p in context.Notifications.OfType<Alert>()
select p;
foreach (var alert in alerts)
{
Assert.IsNotNull(alert);
}
}
I get an error in the PocoAdapter class, claiming that PocoEntity is null is the following method inside my base class's adapter:
public override void PopulatePocoEntity(bool enableProxies)
{
base.PopulatePocoEntity(enableProxies);
PocoEntity.Owner = _Owner.CreatePocoStructure();
if (!(PocoEntity is IEntityProxy))
{
}
}
Any ideas from anyone?
So, after a little more debugging, I think this is related to proxies. Inside PocoAdapterBase we have the following method:
protected PocoAdapterBase(TPocoClass pocoObject)
{
_context = ThreadLocalContext.Current;
bool allowProxies = false;
if (_context != null)
{
allowProxies = _context.EnableChangeTrackingUsingProxies;
}
_pocoEntity = pocoObject ?? (TPocoClass)(allowProxies ? CreatePocoEntityProxy() : CreatePocoEntity());
Init();
InitCollections(allowProxies);
RegisterAdapterInContext();
}
The line that sets _pocoEntity calls CreatePocoEntityProxy, which returns null.
More info as I find it.