Passing computed values with RIA Services - silverlight

I'm trying to figure out the a way to get extra data to be passed with the entities returned from a RIA domain service.
For example, let's say I want to display a DataGrid for "Orders" and include a column for the total items in an order.
Order Num. | Cust. Name | *No. of Items Ordered*
4545 | John | 4
1234 | Mike | 7
On the server side, with a Linq query, I could do:
var query =
from o in entities.Orders
select new OrderWithItemCount { Order = o, ItemCount = o.Items.Count() };
... and this will retrieve my orders along with the Items counts all in one go.
The problem is, I can't find anyway to propagate these results thru a domain service to the Silverlight client. I suppose I could use a standard WCF service, but what's the fun in that?
Update
What turned out to be the actual problem...
I had at one point actually already tried the "Easy way" that Nissan Fan and Florian Lim point out. When I tried it, I wasn't getting all my data. (I also need to include the customer Person in the query to get their name.) It turns out that what I thought was a limitation of RIA Services was actually a limitation of EF 4.0, in that saying entities.Orders.Include("Customer") won't work if you select a new type that isn't an Order. The work around is to explicitly select o.Customer in your select statement, and EF will automatically wire the selected Person into the assiocated property on Order.

Easy way:
Just add extra fields to your Order class (could be done in a partial class) and populate that in your DomainService.
Complicated but more flexible way:
Define OrderWithItemCount as an entity (needs to have a [Key] attribute), then transfer that.
public class OrderWithItemCount
{
[Key]
public int Id { get; set; }
// You need this, so that the DomainContext on the client can put them back together.
public int OrderId { get; set; }
[Include]
[Association("OrderReference", "OrderId", "Id")]
public Order Order { get; set; }
public int ItemCount { get; set; }
public Person Customer { get; set; }
}
public IQueryable<OrderWithItemCount> GetOrdersWithItemCount()
{
var query = from o in entities.Orders
select new OrderWithItemCount
{
OrderId = o.Id,
Order = o,
ItemCount = o.Items.Count,
Customer = o.Customer, // Makes sure EF also includes the Customer association.
};
return query;
}
There may be minor errors in the code, since I cannot test this at the moment, but I recently implemented something similar.

If you are using LINQ to SQL to produce your Domain Service you could simply go into the partial class for Orders and add a Property called NumberOfOrders which returns an Int representing the count. This property would carry through to the client without issue.
internal sealed class OrderMetadata
{
// Metadata classes are not meant to be instantiated.
private OrderMetadata()
{
}
... (property stubs auto-generated)
}
public int NumberOfOrders
{
get { return this.Items.Count; }
}
The reason why you cannot do this the way you demonstrated above is because you cannot marshal across anything but conrete classes (anonyous classes are a no-go). By adding this property to the partial class it will effectively be part of its published signature.

Related

Why am I getting DbUpdateException: OptimisticConcurrencyException?

I have a Category class:
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
}
I also have a Subcategory class:
public class Subcategory
{
public int SubcategoryId { get; set; }
public Category Category { get; set; }
public string SubcategoryName { get; set; }
}
And a Flavor class:
public class Flavor
{
public int FlavorId { get; set; }
public Subcategory Subcategory { get; set; }
public string FlavorName { get; set; }
}
Then I also have Filling and Frosting classes just like the Flavor class that also have Category and Subcategory navigation properties.
I have a Product class that has a Flavor navigation property.
An OrderItem class represents each row in an order:
public class OrderItem
{
public int OrderItemId { get; set; }
public string OrderNo { get; set; }
public Product Product { get; set; }
public Frosting Frosting { get; set; }
public Filling Filling { get; set; }
public int Quantity { get; set; }
}
I'm having issues when trying to save an OrderItem object. I keep getting DbUpdateException: An error occurred while saving entities that do not expose foreign key properties for their relationships. with the Inner Exception being OptimisticConcurrencyException: Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. I've stepped through my code several times and I can't find anything that modifies or deletes any entities loaded from the database. I've been able to save the OrderItem, but it creates duplicate entries of Product, Flavor, Subcategory and Category items in the DB. I changed the EntityState of the OrderItem to Modified, but that throws the above exception. I thought it might have been the fact that I have Product, Frosting and Filling objects all referencing the same Subcategory and Category objects, so I tried Detaching Frosting and Filling, saving, attaching, changing OrderItem entity state to Modified and saving again, but that also throws the above exception.
The following statement creates duplicates in the database:
db.OrderItems.Add(orderItem);
Adding any of the following statements after the above line all cause db.SaveChanges(); to throw the mentioned exception (both Modified and Detached states):
db.Entry(item).State = EntityState.Modified;
db.Entry(item.Product.Flavor.Subcategory.Category).State = EntityState.Modified;
db.Entry(item.Product.Flavor.Subcategory).State = EntityState.Modified;
db.Entry(item.Product.Flavor).State = EntityState.Modified;
db.Entry(item.Product).State = EntityState.Modified;
Can someone please give me some insight? Are my classes badly designed?
The first thing to check would be how the entity relationships are mapped. Generally the navigation properties should be marked as virtual to ensure EF can proxy them. One other optimization is that if the entities reference SubCategory then since SubCats reference a Category, those entities do not need both. You would only need both if sub categories are optional. Having both won't necessarily cause issues, but it can lead to scenarios where a Frosting's Category does not match the category of the Frosting's SubCategory. (Seen more than enough bugs like this depending on whether the code went frosting.CategoryId vs. frosting.SubCategory.CategoryId) Your Flavor definition seemed to only use SubCategory which is good, just something to be cautious of.
The error detail seems to point at EF knowing about the entities but not being told about their relationships. You'll want to ensure that you have mapping details to tell EF about how Frosting and SubCategory are related. EF can deduce some of these automatically but my preference is always to be explicit. (I hate surprises!)
public class FrostingConfiguration : EntityTypeConfiguration<Frosting>
{
public FlavorConfiguration()
{
ToTable("Flavors");
HasKey(x => x.FlavorId)
.Property(x => x.FlavorId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(x => x.SubCategory)
.WithMany()
.Map(x => x.MapKey("SubCategoryId");
}
}
Given your Flavor entity didn't appear to have a property for the SubCategoryId, it helps to tell EF about it. EF may be able to deduce this, but with IDs and the automatic naming conventions it looks for, I don't bother trying to remember what works automagically.
Now if this is EF Core, you can replace the .Map() statement with:
.ForeignKey("SubCategoryId");
which will set up a shadow property for the FK.
If SubCats are optional, then replace HasRequired with HasOptional. The WithMany() just denotes that while a Flavor references a sub category, SubCategory does not maintain a list of flavours.
The next point of caution is passing entities outside of the scope of the DBContext that they were loaded. While EF does support detaching entities from one context and reattaching them to another, I would argue that this practice is almost always far more trouble than it is worth. Mapping entities to POCO ViewModels/DTOs, then loading them on demand again when performing updates is simpler, and less error-prone then attempting to reattach them. Data state may have changed between the time they were initially loaded and when you go to re-attach them, so fail-safe code needs to handle that scenario anyways. It also saves the hassle of messing around with modified state in the entity sets. While it may seem efficient to not load the entities a second time, by adopting view models you can optimize reads far more efficiently by only pulling back and transporting the meaningful data rather than entire entity graphs. (Systems generally read far more than they update) Even for update-heavy operations you can utilize bounded contexts to represent large tables as smaller, simple entities to load and update a few key fields more efficiently.

DateCreated or Modified Column - Entity Framework or using triggers on SQL Server

After I read one question in attached link, I got a sense of how to set DateCreated and DateModified columns in Entity Framework and use it in my application. In the old SQL way though, the trigger way is more popular because is more secure from DBA point of view.
So any advice on which way is the best practice? should it be set in entity framework for the purpose of application integrity? or should use trigger as it make more sense from data security point of view? Or is there a way to compose trigger in entity framework? Thanks.
EF CodeFirst: Rails-style created and modified columns
BTW, even though it doesn't matter much, I am building this app using ASP.NET MVC C#.
Opinion: Triggers are like hidden behaviour, unless you go looking for them you usually won't realise they are there. I also like to keep the DB as 'dumb' as possible when using EF, since I'm using EF so my team wont need to maintain SQL code.
For my solution (mix of ASP.NET WebForms and MVC in C# with Business Logic in another project that also contains the DataContext):
I recently had a similar issue, and although for my situation it was more complex (DatabaseFirst, so required a custom TT file), the solution is mostly the same.
I created an interface:
public interface ITrackableEntity
{
DateTime CreatedDateTime { get; set; }
int CreatedUserID { get; set; }
DateTime ModifiedDateTime { get; set; }
int ModifiedUserID { get; set; }
}
Then I just implemented that interface on any entities I needed to (because my solution was DatabaseFirst, I updated the TT file to check if the table had those four columns, and if so added the interface to the output).
UPDATE: here's my changes to the TT file, where I updated the EntityClassOpening() method:
public string EntityClassOpening(EntityType entity)
{
var trackableEntityPropNames = new string[] { "CreatedUserID", "CreatedDateTime", "ModifiedUserID", "ModifiedDateTime" };
var propNames = entity.Properties.Select(p => p.Name);
var isTrackable = trackableEntityPropNames.All(s => propNames.Contains(s));
var inherits = new List<string>();
if (!String.IsNullOrEmpty(_typeMapper.GetTypeName(entity.BaseType)))
{
inherits.Add(_typeMapper.GetTypeName(entity.BaseType));
}
if (isTrackable)
{
inherits.Add("ITrackableEntity");
}
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", String.Join(", ", inherits)));
}
The only thing left was to add the following to my partial DataContext class:
public override int SaveChanges()
{
// fix trackable entities
var trackables = ChangeTracker.Entries<ITrackableEntity>();
if (trackables != null)
{
// added
foreach (var item in trackables.Where(t => t.State == EntityState.Added))
{
item.Entity.CreatedDateTime = System.DateTime.Now;
item.Entity.CreatedUserID = _userID;
item.Entity.ModifiedDateTime = System.DateTime.Now;
item.Entity.ModifiedUserID = _userID;
}
// modified
foreach (var item in trackables.Where(t => t.State == EntityState.Modified))
{
item.Entity.ModifiedDateTime = System.DateTime.Now;
item.Entity.ModifiedUserID = _userID;
}
}
return base.SaveChanges();
}
Note that I saved the current user ID in a private field on the DataContext class each time I created it.
As for DateCreated, I would just add a default constraint on that column set to SYSDATETIME() that takes effect when inserting a new row into the table.
For DateModified, personally, I would probably use triggers on those tables.
In my opinion, the trigger approach:
makes it easier; I don't have to worry about and remember every time I save an entity to set that DateModified
makes it "safer" in that it will also apply the DateModified if someone finds a way around my application to modify data in the database directly (using e.g. Access or Excel or something).
Entity Framework 6 has interceptors which can be used to set created and modified. I wrote an article how to do it: http://marisks.net/2016/02/27/entity-framework-soft-delete-and-automatic-created-modified-dates/
I agree with marc_s - much safer to have the trigger(s) in the database. In my company's databases, I require each field to have a Date_Modified, Date_Created field, and I even have a utility function to automatically create the necessary triggers.
When using with Entity Framework, I found I needed to use the [DatabaseGenerated] annotation with my POCO classes:
[Column(TypeName = "datetime2")]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? Date_Modified { get; set; }
[Column(TypeName = "datetime2")]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? Date_Created { get; set; }
I was attempting to use stored procedure mapping on an entity, and EF was creating #Date_Modified, #Date_Created parameters on my insert/update sprocs getting the error
Procedure or function has too many arguments specified.
Most of the examples show using [NotMapped], which will allow select/insert to work, but then those fields will not show up when that entity is loaded!
Alternately you can just make sure any sprocs contain the #Date_Modified, #Date_Created parameters, but this goes against the design of using triggers in the first place.

WCF RIA Services / Linq-to-SQL: include property from foreign tables

Say you have the following related tables (Stores -> Categories -> Products)
Stores
Categories
Products
And I want to create a grid to edit Products. This is straightforward with RIA Services. But what if I also want to show StoreName from Stores and CategoryName from Categories in my Products list? The two extra columns should be readonly.
How can this be implemented?
Update: I'm trying to do this in it's simplest form. That is no ViewModel, only drag'n drop, code (if any) will go in codebehind. I'm using Ling2Sql and returning the default implementation for the GetProducts query.
Regards
Larsi
How do you have this set up? Are you binding to a ViewModel or just using the code behind? Is the web service sending back a list of Product LINQ object or are you doing something else?
There are a variety of options but it really depends on what you're trying to do.
The simplest way to do it is to annotate your metadata file for the products and let the grid generate the columns for you.
For instance, your tables will probably look something like this:
Product
int Id;
string ProductName;
int CategoryId;
Category
int Id;
string CategoryName;
int StoreId;
Store
int Id;
string StoreName;
Now, when you create your service, you can include the 3 tables/entities from your domain model and have it generate the metadata file for you. In that file, annotate the objects correctly like so:
internal sealed class ProductMetadata
{
[Key]
[Bindable(false)]
[Display(AutogenerateField=false)]
public int Id { get; set; }
[Bindable(true, BindingDirection.TwoWay)]
[Display(Name="Product")]
[StringLength(20, MinimumLength=3)]
public string ProductName { get; set; }
[Bindable(false)]
[Display(AuteogenerateField=false)]
public Category Category { get; set; }
[Required]
[Bindable(false)]
[Display(AutogenerateField=false)]
public CategoryId { get; set; }
}
You can do the same to your other objects' metadata.
The only other thing you might have to do is add 2 other columns to your grid, and have them map to Product.Category.CategoryName and Product.Category.Store.StoreName

Trouble updating one to many relationships when using ria services with nhibernate

I am working on a silverlight application and I am using RIA data services and nHibernate.
Currently, I have an entity with a one to many relationship to another entity.
public class Employer {
[Key]
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class Person {
[Key]
public virtual int Id { get; set; }
public virtual string Name { get; set; }
[Include]
[Association("PersonCurrentEmployer", "CurrentEmployerId", "Id", IsForeignKey = true)]
public virtual Employer CurrentEmployer { get; set; }
public virtual int? CurrentEmployerId { get; set; }
}
The property CurrentEmployerId is set for no insert and no update in the mappings.
On the Silverlight side, I set the CurrentEmployer property of the person to an existing employer on the client side submit the changes.
personEntity.CurrentEmployer = megaEmployer;
dataContext.SubmitChanges();
On the server side, the person entity's CurrentEmployerId is set to megaEmployer.Id but the CurrentEmployer is null. Because I am using the CurrentEmployer property and not the CurrentEmployerId to save the relationship, the relationship isn't changed.
Is there a way to force RIA to send the CurrentEmployer object with the save or do I have to use the CurrentEmployerId on the server side to load the employer and set it to the CurrentEmployer?
The reason you're not seeing your CurrentEmployer on the client side is because you don't have your association setup correctly.
RIA services doesn't work with references in the usual way so referencing your Employer on the client side doesnt work. RIA services works with entity sets and creates the "references" based on the association attributes. Your employer needs a property with an association back to the Person as follows.
public class Employer
{
private Person person;
[Key]
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int PersonID { get; set; }
[Include]
[Association("PersonCurrentEmployer", "PersonID", "Id", IsForeignKey = false)]
public virtual Person Person {
get
{
return this.person;
}
set
{
this.person = value;
if (value != null)
{
this.PersonID = value.Id;
}
}
}
}
Is there a way to force RIA to send the CurrentEmployer object with the save or do I have to use the CurrentEmployerId on the server side to load the employer and set it to the CurrentEmployer?
I'm running into this problem as well. Basically, you either have to use the [Composition] attribute (which I wouldnt' recommend), or load the entity from the database, server-side. Composition muddies up the client data model and doesn't take care of all cases you need to worry about. (there is a lot more on Composition in the RIA forums.silverlight.net)
[UPDATE] Once you implement 2nd level cache, the worry of reading supporting entities from the database mostly goes away, as they will be loaded from cache. Also, if you only need a proxy for NHibernate to not complain, then look into Get/Load (can never remember which) .. which will return an NH proxy and will result in a single-column-and-entity select from the database. (If you try to access another property of the proxy, NH will select the rest. you can find more on this on Ayende's blog..)[/UPDATE]
The biggest problem I'm having is getting NHib to actually save and load the relationship. (I'm also using Fluent). The response from the responsible parties has so far been "waah, you can't do that. it looks like RIA wasn't developed with NHib in mind" .. which is a crap answer, IMHO. Instead of helping me figure out how to map it, they're telling me i'm doing it wrong for having a ForeignKey in my entity (NHib shouldn't care that i have my FK in my entity) ...
I want to share what I did to make this work, because 'official' support for this scenario was ... let's just say unhelpful at best, and downright rude at worst.
Incidentally, you had the same idea I had: making the Foreign Key not insert/update. BUT, I've also made it Generated.Always(). this way it will always read the value back.
Additionally, I override DomainService.Submit() and DomainService.ExecuteChangeSet(). I start an NHibernate Transaction in the Submit (though I'm not yet sure this does what I expect it does).
Instead of putting my save logic in the InsertSomeEntity() or UpdateSomeEntity() methods, I'm doing it all inside ExecuteChangeSet. this is because of NHibernate, and its NEED to have the entity graph fully-bi-directional and hydrated out prior to performing actions in NHibernate. This includes loading of entities from the database or session when a child item comes across the wire from RIA services. (I started down the path of writing methods to get the various other pieces of the graph as those specialized methods needed them, but I found it easier to do it all in a single method. Moreover, I was running into the problem of RIA wanting me to perform the insert/updates against the child objects first, which for new items is a problem.)
I want to make a comment about the composition attribute. I still stand by my previous comment about not recommending it for standard child entity collections, HOWEVER, it works GREAT for supporting NHibernate Components, because otherwise RIA will never send back the parent instance (of the composition), which is required for NHibernate to work right.
I didn't provide any code here because i would have to do some heavy redacting, but it's not a problem for me to do if you would like to see it.

Fetch Tags and Tag count using HQL on SQL Server 2008

I'm implementing tagging on a particular entity, using NHibernate on SQL Server 2008. The structure I have now is, simplifying, like this:
public class Entity {
public Guid Id { get; set; }
}
public class Tag {
public Guid Id { get; set; }
public string Name { get; set; }
}
public class TagAssoc {
public Tag LinkedTag { get; set; }
public Entity LinkedEntity { get; set; }
//User
//Other properties
}
Nothing exotic: an entity can be tagged multiple times with the same tag, since the association also includes data about the user that tagged the entity and other stuff.
Now, I'm trying to fetch a list of tags of a particular entity, with the counts of how many times the tag has been applied. Something like this in HQL:
select tass.LinkedTag, count(tass.LinkedTag)
from TagAssoc as tass left outer join tass.LinkedTag as t
group by tass.LinkedTag
This generates the following SQL query:
select tag1_.Id as Id0_, tag1_.Name as Name0_, tag1_.Id as x0_0_, count_big(tag1_.Id) as x1_0_
from BandTags tagassoc0_ left outer join Tags tag1_ on tagassoc0_.TagId=tag1_.Id
group by tag1_.Id
This looks correct, but won't work in SQL Server 2008, because the Name property of Tag is not included in a "group by" clause. To make it work, I have to manually adjust the group by clause in order to include all properties of the Tag class:
select tass.LinkedTag, count(tass.LinkedTag)
from TagAssoc as tass left outer join tass.LinkedTag as t
group by tass.LinkedTag.Id, tass.LinkedTag.Name
But this depends on the properties of the Tag class and therefore would have to be updated every time the class is changed.
Is there some other way to make the first HQL query work? Perhaps some HQL syntax that automatically makes the "group by" properties explicit?
Thanks
It doesn't appear that there is any way to make NHibernate determine the group by properties automatically. The documentation even seems to imply this in the example HQL they give for an aggregate function:
select cat, count( elements(cat.Kittens) )
from Eg.Cat cat group by cat.Id, cat.Weight, ...
There they also explicitly specify the properties of Cat.
If you want to dynamically build a query that does not need an update every time the class changes, I think you're stuck with Reflection and the Criteria interface.
ProjectionList list = Projections.ProjectionList();
foreach (PropertyInfo prop in typeof(Tag).GetProperties())
{
list.Add(Projections.GroupProperty(prop.Name));
}
list.Add(Projections.Property("LinkedTag"));
list.Add(Projections.Count("LinkedTag"));
session.CreateCriteria(typeof(TagAssoc)).SetProjection(list).List();
I haven't tried this so it may or may not work or might need some tweaking, but you get the idea. You might decide the Tag class won't change enough to be worth the trouble.

Resources