ok, I know this is asked a million times, but I still can't seem to find a working solution. Hopefully I'm just missing something.
Using entity with silverlight, through RIA
I have a many to many table structure in my DB.
[System] - [SystemUsers] - [Users]
The bridge table just has the two IDs so naturaly entity doens't include it in the model.
I know RIA doesn't like many to many relationships, so I've added Association attribute tags to the collection, and can now at least see the collection property on the client side.
[Association("DMSSystem_Users", "DMSSystemId", "UserId")]
[Include]
[Composition]
public EntityCollection<Users> Users { get; set; }
In my domain service I've tried including the users:
public IQueryable<DMSSystem> GetSystem()
{
return this.ObjectContext.DMSSystem.Include("Users");
}
I never get the users on the client side Is there something else I am missing to get the users to be inculded and sent to the client?
RIA with EF has a dislike for M:M relationships. What you need to do is to help RIA see the M:M as two 1:M relationships.
On your link table add an extra column (I add something like IgnoreThisField) and make it a bit type.
When EF sees this table it will now interpret it differently allowing you to handle the M:M relationship.
I don't know Entity Framework but here's how this works in RIA with NHibernate...
My model is Users - UserRoleGrant - Role. You'll have to do the translation to your model in your head.
The important parts of the following code are... ensure you have the correct association names on your model, ensure you have the correct property names set in your associations, set the UserID property on the UserRoleAssociation when you set it's User property. If you don't set this ID you wont have access to the related entity via the assocation property.
You probably don't need the Composition attribute either but you might so read this to find out... http://ria.feedables.com/story/4583193/Composition-Support-in-RIA-Services
public class User
{
...snip...
[Include]
[Association("UserToRoleAssociation", "Id", "UserId", IsForeignKey = false)]
public virtual IList<UserRoleAssociation> RoleGrants
{
get
{
return this.roleGrants;
}
}
}
public class UserRoleAssociation
{
/// <summary>
/// Backing field for User
/// </summary>
private User user;
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The relationships id.</value>
[Key]
public virtual long Id { get; set; }
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The assigned users id.</value>
public virtual long UserId { get; set; }
/// <summary>
/// Gets or sets the user.
/// [Association("UserRoleGrants", "UserId", "Id", IsForeignKey = false)]
/// </summary>
/// <value>The user who has been granted this operation.</value>
[Include]
[Association("UserToRoleAssociation", "UserId", "Id", IsForeignKey = true)]
public virtual User User
{
get
{
return this.user;
}
set
{
this.user = value;
if (value != null)
{
this.UserId = value.Id;
}
}
}
}
Just ran across this: http://m2m4ria.codeplex.com/
It requires some setup, but exposes a view to the client which solves the whole many-to-many issue, without modifying your database or entity model. I found it very useful.
Related
DbSet<T>.Local provides an ObservableCollection that you can bind to WPF controls. In my case, I'm binding it to a grid.
If I was using view models, I would have them implement INotifyDataErrorInfo or IDataErrorInfo, and then write Fluent Validation validators to handle validation.
But here I'm binding to the models via DbSet<T>.Local. How should I handle validation?
Should I implement INotifyDataErrorInfo or IDataErrorInfo on my models?
Or is there some alternative?
If you are application is a thick client, meaning all layers of the application are on a single physical machine, then you should implement IDataErrorInfo on your models.
On the other hand, if your application is a thin multi-tired application, meaning that your model is implemented on a server and you are using the WPF desktop application to communicate with the server side code then you should implement INotifyDataErrorInfo.
Yes I think implement implementing IValidatableObject in POCO Models makes sense.
And you have probably noticed public ObservableCollection<TEntity> Local { get; }
There are several ways to do this. So a some homework.
As some background , might be useful
Then check this out
So we know EF has the concept of triggering Validations.
Configuration.ValidateOnSaveEnabled = true;
EF will also call IEnumerable<ValidationResult> ValidateInstance();
from interface IValidatableObject
if your entity POCO implements IValidatableObject
EF will trigger validate on SAve.
You can also easily trigger validations when you need to.
This all neatly links in with UoW concept.
public IEnumerable<DbEntityValidationResult> GetDbValidationErrors() { return
Context.GetValidationErrors(); } // Standard Context call get the problems
which can be used
catch (Exception ex) {....
var x = GetDbValidationErrors();
//....
So i think you are on the right track...
EDIT: Add SAMPLE POCOBase and demonstrate trigger validation.
public interface IFBaseObject : IValidatableObject {
// a POCO object must implement the VALIDATE method from IValidatableObject
bool IsValidInstance();
IEnumerable<ValidationResult> ValidateInstance();
}
public abstract class BaseObject : IFBaseObject {
// .... base object stuff removed....
/// <summary>
/// Get called every a Validation is trigger on an object. Here we return and Empty resultset to start with.
/// If you override ALWAYS call Base first and Continue to add your own results as desired.
/// Never fail to call base First !
/// </summary>
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
# region Sample implementation for a POCO thats needs validation
/* Sample Implementation a sub type POCO might use.
var validationResult = base.Validate(validationContext).ToList();
if (true) // the condition that leads to a validation error
{
var memberList = new List<string> { "PropertyName" }; // the name of the offending property
var error = new ValidationResult("Error text goes here", memberList); // use teh textpool !!! no hardcoded texts
validationResult.Add(error);
}
return validationResult;
*/
# endregion
// now back in the base Class.
var validationResult = new List<ValidationResult>();
// hand back a list that is empty so errors if any can be added by SUBclasses
// we can check any Poco that implements a certain interface centrally.
var thisIsKeyGuid = this as IFKeyGuid;
if (thisIsKeyGuid != null) {
if (thisIsKeyGuid.Id == Guid.Empty) {
validationResult.Add(new ValidationResult("Id is required", new List<string>() {"Id"}));
}
}
return validationResult;
}
/// <summary>
/// Allows explicit triggering of Validation and returns a TRUE or false answer. Call anytime
/// </summary>
/// <returns></returns>
public virtual bool IsValidInstance() {
List<ValidationResult> vResults;
return SelfValidation(out vResults);
}
/// <summary>
/// Calls Self Validation which uses THIS object as the context for validation.
/// This means you can trigger a validation without first declaring a validation context.
/// IValidatableObject is effectively called for you. Witch causes "Validate" to be called
/// </summary>
/// <returns></returns>
public IEnumerable<ValidationResult> ValidateInstance() {
List<ValidationResult> vResults;
SelfValidation(out vResults);
return vResults;
}
/// <summary>
/// Although SelfValidation is defined at BaseObject level, this triggers the VALIDATION process on the current Object
/// see http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.validator.aspx for mroe details
/// So if a POCO Object has overridden public virtual IEnumerable of ValidationResult Validate(ValidationContext validationContext)
/// then this method will be called. It should of course call :base.Validate
/// </summary>
/// <returns>true or false</returns>
private bool SelfValidation(out List<ValidationResult> vResults) {
var vc = new ValidationContext(this, null, null);
vResults = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(this, vc, vResults, true);
return isValid;
}
}
Ok, this is my RIA Services data contract:
public class ZipLocationDC
{
[Key]
public String ZipCode { get; set; }
[Editable(false)]
public double Latitude { get; set; }
[Editable(false)]
public double Longitude { get; set; }
}
I have hundreds other entities very similar to this (i.e. simple classes with little more than primitive-typed properties). With this one for some reason, I get the following exception:
Operation named 'GetZipLocation' does not conform to the required
signature. Return types must be an entity or complex type, a
collection of entities or complex types, or one of the predefined
serializable types.
What am I doing wrong? I don't know why you would need it, but here is my service operation:
[Invoke]
public ZipLocationDC GetZipLocation(String a_strZipCode)
{
var zipCodes = from zipCode in ObjectContext.ZipCodes
where zipCode.Code == a_strZipCode
select zipCode;
if (!zipCodes.Any())
return null;
var dLatitude = zipCodes.Average(i => i.Latitude);
var dLongitude = zipCodes.Average(i => i.Longitude);
return new ZipLocationDC
{
ZipCode = a_strZipCode,
Latitude = dLatitude,
Longitude = dLongitude
};
}
I'm seriously getting tired with RIA Services. I also use straight up WCF, but with RIA I get strange problems like this all the time. Its almost not worth using it.
This is so dumb and why I am switching over to WCF as soon as I can. I had to include my data contract (ZipLocationDC) as the result of a query operation. I just return null. I added this code to my service definition.
/// <summary>
/// This method does nothing but expose ZipLocationDC as an read-only entity.
/// </summary>
/// <returns>Null.</returns>
[Query]
public IQueryable<ZipLocationDC> GetZipLocations()
{
return null;
}
RIA Services is so painfully limiting. I know it does stuff for you, but its been more of a problem than a helper.
Look at this complex type, which is basically a DTO that wraps some entities. I don't need to track these entities or use the for updating or any of that stuff, I just want to send them down to the client. The stuff at the top are non-entities just to let me know that I'm not crazy.
public class ResultDetail
{
// non entities (some are even complex) - this works GREAT!
public string WTF { get; set; }
public IEnumerable<int> WTFs { get; set; }
public SomethingElse StoneAge { get; set; }
public IEnumerable<SomethingElse> StoneAgers { get; set; }
// these are entities - none of this works
public EntityA EntityA { get; set; }
public IEnumerable<EntityB> EntityB { get; set; }
}
public class SomethingElse
{
public int ShoeString { get; set; }
}
Now look at this:
http://i.snag.gy/tI9O9.jpg
Not a single entity property shows up on the client side generated types. Are there attributes or something that I can or do I really need to create DTO objects for every one of these entity types? There are more than 2 as in my sample and they have many properties.
By the way these entity types have been generated on the client because of the normal query operations in the domain service that work with them.
This is not possible as current Ria services framework is mainly designed for tracking entities, and for Ria services it is not possible to detect which properties to serialized and which to note, since every entity has navigation properties, serializing properties may cause infinite loops or long loops as there is no control over how to navigate object graph.
Instead you are expected to program your client in such way so that you will load relations on demand correctly.
I am struggling with returning a complex type from my services layer. It doesnt seem to be accessible from my object context.
This is the query in the service layer. All compiling fine.
public IQueryable<USP_GetPostsByThreadID_Result> uspGetPostsByThreadID(int ThreadID)
{
return this.ObjectContext.USP_GetPostsByThreadID(ThreadID).AsQueryable();
}
When I try and call it from my client, the ForumContext is not seeing it. I checked the client generated file and nothing similar is being generated. Help!!!
The name of your method may not meet the expected convention for queries. Try one or both of the following:
Add the [Query] attribute
Rename the method to GetUspPostsByThreadID
Result:
[System.ServiceModel.DomainServices.Server.Query]
public IQueryable<USP_GetPostsByThreadID_Result> GetUspPostsByThreadID(int ThreadID)
{
return this.ObjectContext.USP_GetPostsByThreadID(ThreadID).AsQueryable();
}
Its very common to have a stored procedure returning data from multiple tables. The return type doesn't fit well under any of the Entity Types(Tables). Therefore if we define Complex Type as the return collection of objects from Stored Procedure invocation, it becomes quite a powerful tool for the developer.
Following these steps I have achieved successfully the configuration of complex type on a sample AdventureWorks database.
1. Refer the picture and ensure the Stored procedure and function import is done.
2. Add the Domain Service name it as AdventureDomainService.
3. Now its time to define the tell the RIA services framework to identify my Complex Type as Entity Type. To be able to do this, we need to identify a [Key] DataAnnotation. Entity types provide data structure to the application's data model and by design, each entity type is required to define a unique entity key. We can define key on one property or a set of properties in metadata class file AdventureDomainService.metadata.cs
First define the class then add MetadatatypeAttribute like :
[MetadataTypeAttribute(typeof(CTEmployeeManagers.CTEmployeeManagersMetadata))]
public partial class CTEmployeeManagers
{
internal sealed class CTEmployeeManagersMetadata
{
private CTEmployeeManagersMetadata() { }
[Key]
public int EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int ManagerID { get; set; }
public string ManagerFirstName { get; set; }
public string ManagerLastName { get; set; }
}
}
Define the Domain service method to return the collection of objects/entities for populating the Silverlight Grid or any other data consuming controls.
public IQueryable<CTEmployeeManagers> GetEmployeeManagers(int empId)
{
return this.ObjectContext.GetEmployeeManagers(empId).AsQueryable();
}
We define IQueryable if we are to fetch the records from datasources like SQL, whereas we define IEnumerable if we are to fetch the records from in memory collections,dictionaty,arrays.lists, etc.
Compile the server side to generate the client proxy.
In the Silverlight side open the MainPage.xaml or wherever the datagrid is put, then add following namespaces :
using System.ServiceModel.DomainServices.Client;
using SLBusinessApplication.Web;
using SLBusinessApplication.Web.Services;
..
Load the data and display:
public partial class MyPage : Page
{
AdventureDomainContext ctx = new AdventureDomainContext();
public MyPage()
{
InitializeComponent();
LoadOperation loadOp = this.ctx.Load(this.ctx.GetEmployeeManagersQuery(29));
myGrid.ItemsSource = loadOp.Entities;
}
// Executes when the user navigates to this page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
}
That is all that is needed to do.
It has to be part of an entity. Complex types cannot be returned by themselves
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.