Need for using different DTOs on web api resource - rest

I have couple of DTOs that I am using in WEB API and I have noticed that I am reusing same properties over and over again.
For example
public class Response
{
public int Id {get;set;}
public DateTime CreatedOn {get;set;}
public string Name {get;set;}
public string Code {get;set;}
}
public class InsertRequest
{
public string Name {get;set;}
public string Code {get;set;}
}
Is there really a need to specify InsertRequest for a resource, since DTOs are processed later in the code? It could be misleading to have property Id available in code even if Id would not be inserted.
On the other hand if Id is declared as nullable, it can be misleading since Id in Response should not be nullable, so I am in doubt should these two request be split or should they all be in one representing a resource?

The situation you describe is pretty common for situations where you have to create / update entities.
I would recommend you keep yours as they are. CreatedOn makes no sense when you want to update and Id, well, it'll never change once created, so again, it makes no sense to have it in a change entity. Plus, chances are that you will provide your Id in the route so the actual entity doesn't need it anyway:
PUT against:
www.somedomain.com/entity/id
entity doesn't need id field as its coming from the URI.
Yes, you can argue that you will end up with entities with duplicate fields, but at the same time you'll have a clean API which makes sense and that's more important as this is what the clients will be seeing and using.
One more thing, I am in favor of keeping a clear naming convention so if one entity is InsertRequest then the other will be UpdateRequest.

Related

EF Core Code First - one to one conditional relationship

I'm not sure if this relationship has a name, so maybe I'm just missing it. I didn't find anything in the docs that looked like this.
I have a many-to-one relationship, sort of, but only one relationship matters, so it's kind of a one-to-one, but on a condition at the many side.
Equipment has many Contracts, but at most one active Contract.
Contract always has one Equipment, and has a reference to the Equipment. That side works just fine. I have business logic to prevent more than one Contract for an Equipment being active.
public class EquipmentModel{
public int Id {get; set;}
//...other properties
}
public class ContractModel{
public int Id {get;set;}
public EquipmentModel Equipment {get;set;}
public bool Active {get;set;}
//...other properties
}
This means I can do a _contracts.Include(x => x.Equipment) and get the equipment entity for the contract.
But I can't wrap my head around the relationship the other direction. I don't want to just put all the Contacts on the Equipment entity, I don't have a need for them and it seems like overkill.
I suppose I could put the Contract entity on the Equipment model and manually manage the relationship in business logic, but I'm hoping Entity Framework has a better way to designate this.
I could also pull back all contracts and use some business logic to filter down to the active one, but that's also less than ideal.
public IEnumerable<ContractModel> Contracts {get;set;}
Ideally, I'd want to be able to do something like _equipment.Include(x => x.ActiveContract) and have the one Contract with an Active == true automatically. Is this a pattern that exists within EF Core?
The easiest way with that design is to use a Filtered Include,
_equipment.Include(x => x.Contracts.Where(c => c.Active))
You can reuse this expression by creating a method on your DbContext, that returns the base query with the Include, eg
public IQueryable<Equipment> GetEquipment() => this.Set<Equipment>().Include(x => x.Contracts.Where(c => c.Active));
Which can be further filtered by the calling code before running the query, eg
var equips = db.GetEquipment().Where(e => e.Name == equipName).ToList();
If you feel strongly about it you can even omit the DbSet<Equipment> property on the DbContext (registering Equipment as an Entity in OnModelCreating), and replace it with
public IQueryable<Equipment> Equipment => this.Set<Equipment>().Include(x => x.Contracts.Where(c => c.Active));

How do I update a model in a ReliableDictionary?

Consider this mock scenario where I have the following class:
public class Person {
public Guid Id {get;set;}
public string Name {get;set;}
public Address Address {get;set;}
}
I currently have a ReliableDictionary(Guid,Person). I'd like to change my Person model by splitting out the Address, to the following:
public class Person {
public Guid Id {get;set;}
public string Name {get;set;}
}
public class Address {
public Guid Id {get;set;}
public string Street {get;set;}
...
}
I would then have two ReliableDictionaries, one for persons and one for addresses.
What is the proper way to split these entities and migrate the data during an upgrade? I feel like there used to be documentation on this, but I can no longer find it.
Thanks!
An important requirement is that your model needs to be backward compatible. A stored model should be able to load, no matter the changes to your model.
Converting all stored models requires you to loop through the stored models, make changes and store them again. You need to write specific code for this. This could be included in the microservice, or you could create a temporary microservice which calls the microservice which holds the data. After the conversion is done you can remove the code or the microservice.
You could also do this 'on the fly'. Whenever a model is loaded during production you check the version number (you need to specify a version field on your model). If the version is below a certain value, convert it.

Adding Navigation property breaks breeze client-side mappings (but not Server Side EF6)

I have an application that I developed standalone and now am trying to integrate into a much larger model. Currently, on the server side, there are 11 tables and an average of three navigation properties per table. This is working well and stable.
The larger model has 55 entities and 180+ relationships and includes most of my model (less the relationships to tables in the larger model). Once integrated, a very strange thing happens: the server sends the same data, the same number of entities are returned, but the exportEntities function returns a string of about 150KB (rather than the 1.48 MB it was returning before) and all queries show a tenth of the data they were showing before.
I followed the troubleshooting information on the Breeze website. I looked through the Breeze metadata and the entities and relationships seem defined correctly. I looked at the data that was returned and 9 out of ten entities did not appear as an object, but as a function: function (){return e.refMap[t]} which, when I expand it, has an 'arguments' property: Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them.
For reference, here are the two entities involved in the breaking change.
The Repayments Entity
public class Repayment
{
[Key, Column(Order = 0)]
public int DistrictId { get; set; }
[Key, Column(Order = 1)]
public int RepaymentId { get; set; }
public int ClientId { get; set; }
public int SeasonId { get; set; }
...
#region Navigation Properties
[InverseProperty("Repayments")]
[ForeignKey("DistrictId")]
public virtual District District { get; set; }
// The three lines below are the lines I added to break the results
// If I remove them again, the results are correct again
[InverseProperty("Repayments")]
[ForeignKey("DistrictId,ClientId")]
public virtual Client Client { get; set; }
[InverseProperty("Repayments")]
[ForeignKey("DistrictId,SeasonId,ClientId")]
public virtual SeasonClient SeasonClient { get; set; }
The Client Entity
public class Client : IClient
{
[Key, Column(Order = 0)]
public int DistrictId { get; set; }
[Key, Column(Order = 1)]
public int ClientId { get; set; }
....
// This Line lines were in the original (working) model
[InverseProperty("Client")]
public virtual ICollection<Repayment> Repayments { get; set; }
....
}
The relationship that I restored was simply the inverse of a relationship that was already there, which is one of the really weird things about it. I'm sure I'm doing something terribly wrong, but I'm not even sure at this point what information might be helpful in debugging this.
For defining foreign keys and inverse properties, I assume I must use either data annotations or the FluentAPI even if the tables follow all the EF conventions. Is either one better than the other? Is it necessary to consistently choose one approach and stay with it? Does the error above provide any insight as to what I might be doing wrong? Is there any other information I could post that might be helpful?
Breeze is an excellent framework and has the potential to really increase our reach providing assistance to small farmers in rural East Africa, and I'd love to get this prototype working.
THanks
Ok, some of what you are describing can be explained by breeze's default behavior of compressing the payload of any query results that return multiple instances of the same entity. If you are using something like the default 'json.net' assembly for serialization, then each entity is sent with an extra '$id' property and if the same entity is seen again it gets serialized via a simple '$ref' property with the value of the previously mentioned '$id'.
On the breeze client during deserialization these '$refs' get resolved back into full entities. However, because the order in which deserialization is performed may not be the same as the order that serialization might have been performed, breeze internally creates deferred closure functions ( with no arguments) that allow for the deferred resolution of the compressed results regardless of the order of serialization. This is the
function (){return e.refMap[t]}
that you are seeing.
If you are seeing this value as part of the actual top level query result, then we have a bug, but if you are seeing this value while debugging the results returned from your server, before they have been returned to the calling function, then this is completely expected ( especially if you are viewing the contents of the closure before it should be executed.)
So a couple of questions and suggestions
Are you are actually seeing an error processing the result of your query or are simply surprised that the results are so small? If it's just a size issue, check and see if you can identify data that should have been sent to the client and is missing. It is possible that the reference compression is simply very effective in your case.
take a look at the 'raw' data returned from your web service. It should look something like this, with '$id' and '$ref' properties.
[{
'$id': '1',
'Name': 'James',
'BirthDate': '1983-03-08T00:00Z',
},
{
'$ref': '1'
}]
if so, then look at the data and make sure that an '$'id' exists that correspond to each of your '$refs'. If not, something is wrong with your server side serialization code. If the data does not look like this, then please post back with a small example of what the 'raw' data does look like.
After looking at your Gist, I think I see the issue. Your metadata is out of sync with the actual results returned by your query. In particular, if you look for the '$id' value of "17" in your actual results you'll notice that it is first found in the 'Client' property of the 'Repayment' type, but your metadata doesn't have 'Client' navigation property defined for the 'Repayment' type ( there is a 'ClientId' ). My guess is that you are reusing an 'older' version of your metadata.
The reason that this results in incomplete results is that once breeze determines that it is deserializing an 'entity' ( i.e. a json object that has $type property that maps to an actual entityType), it only attempts to deserialize the 'known' properties of this type, i.e. those found in the metadata. In your case, the 'Client' navigation property on the 'Repayment' type was never being deserialized, and any refs to the '$id' defined there are therefore not available.

Auditing many-to-many relationships in Entity Framework?

I have been reading some artices about using the RelationshipManager to gain access to the entries that have related data. It is still unclear to me what the best way to audit when an entity whose related data is added or updated.
Sample Classes:
public class Rfi
{
public Guid Id {get;set;}
public string Number {get;set;}
public virtual ICollection<Attachment> Attachments {get;set;}
}
public Class Attachment
{
public Guid Id {get;set;}
public string Name {get;set;}
public string Description {get;set;}
public string FileName {get;set;}
public string Path {get;set;}
}
Sample Mappings:
public class RfiMapping: EntityTypeConfiguration<Rfi>
{
public Rfimapping()
{
HasMany(r => r.Attachments).WithMany().Map(m =>
{
m.MapLeftKey("RfiId");
m.MapRightKey("AttachmentId");
m.ToTable("Rfi_Attachments");
});
}
}
I am using the Repository and Unit Of Work patterns. My UoW inherits from DbContext. A repository call may look like this:
public void AddAttachmentToRfi(Attachment attachment, Guid rfiId)
{
var rfi = _rfiRepository.FindById(rfiId);
rfi.Attachments.Add(attachment);
_rfiRepository.UnitOfWork.Commit();
}
Is it possible , in an overridden SaveChanges method, to figure out that an Attachment entity was added to an Rfi entity? When I traverse the, say ChangeTracker.Entries, I am not seeing its state being set to modified. Which makes sense, because I am only adding to the relationships and not the entity directly.
I know to cast my DbContext to an IObjectContextAdapter, but I am not sure what I need to do with the RelationshipManager to get the changes made to any of the relationships. I am also curious to know if I were to update an Attachment's Description property later on, if I can still see what changes were made to any related data.
My goal with this is, the user interface for the Rfi allows users to attach files (Rfi is obviously not the only entity that can have attachments). I need to show a history of everything that happens to an Rfi. This means if an attachment is added I need to audit it. If the attachment's data is updated, I need to audit those changes and show that they were updated via the Rfi interface. This may get complicated if that attachment is shared with another entity, but I will cross that road later.
As you say you are not changing any of the entities only the relationship between them.
EF will then convert this to an insert into or delete from the Rfi_Attachments table.
One way to audit this is to add a database trigger that writes an entry to a log table, each time an entry is added or deleted.

How to deal with "computed" property in Entity Framework 4.1?

I have a model as below:
public class Post
{
public int Id {get;set;}
public virtual ICollection<Comment> Comments {get;set;}
public virtual ICollection<Reader> Readers {get;set;}
public int Value {get;set;}
}
The rule is Value = Comments.Count * 2 + Readers.Count.
What is the right and convenient way to deal with the "computed" property of "Value"?
I think it is the best that the "Value" can be calculated and saved automatically when Comments or Readers add/remove element.
but the "DatabaseGeneratedAttribute" seems no use here.
Thank you!
This is not supported. There is no way to make Value available for linq-to-entities queries if it is not mapped to database column. In case of EF using EDMX for mapping this can be sometimes solved by using custom mapped SQL function or model defined function but code first mapping doesn't support anything of that. Other way is to create database view and map your entity to view but in such case entity will be read only.
Once you use .NET code for defining value it is always only client side property computed from data loaded from database. If you don't want to recompute property every time you need observable collections with event handler changing precomputed value each time the collection changes.
DatabaseGenerated attribute just marks your property as generated by database - in such case you cannot change its value and database must ensure that correct value will be stored in your table.
I think your column value is based on two mapped properties. Use NotMappedAttribute to Exclude a Property from the Database Schema and Load values in runtime.
public class Post
{
public int Id {get;set;}
public virtual ICollection<Comment> Comments {get;set;}
public virtual ICollection<Reader> Readers {get;set;}
[NotMapped]
public int Value
{
get return Comments.Count * 2 + Readers.Count;
}
}
You may use the DatabaseGenerated attribute and then create triggers in the db for calculating the Value. You can create the triggers in the migrations, or db seed method.