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.
Related
I have something like this:
public class ModelEntity : Entity
{
public override int Id { get; set; }
public string FileName { get; set; }
}
public class DataTransferObject
{
public int Id { get; set; }
public string FileName { get; set; }
}
And I would like to do something like this:
var model = _fixture.Create<ModelEntity>();
var dto = _fixture.Create<DataTransferObject>().FillWith(model);
Right now I am doing the following but I am not sure if is the right way to do it
var model = _fixture.Create<ModelEntity>();
var dto = model.AsSource().OfLikeness<DataTransferObject>().CreateProxy();
AutoFixture doesn't have a feature like that, but I think there's something better to be learned from this:
AutoFixture was originally built as a tool for Test-Driven Development (TDD), and TDD is all about feedback. In the spirit of GOOS, you should listen to your tests. If the tests are hard to write, you should consider your API design. AutoFixture tends to amplify that sort of feedback, and it may also be the case here.
It sounds like you need to be able to populate a DataTransferObject with values from a ModelEntity instance. Could this suggest that some sort of mapping would be a valuable addition to your API?
Depending on how these types are already coupled, you could consider adding a projection method to your ModelEntity class:
public class ModelEntity : Entity
{
public override int Id { get; set; }
public string FileName { get; set; }
public DataTransferObject ToDataTransferObject()
{
return new DataTransferObject
{
Id = this.Id,
FileName = this.FileName
};
}
}
However, the disadvantage of this approach is that it couples those two types to each other.
If you find that undesirable, you could instead introduce a dedicated Mapper Service, which can map a ModelEntity instance to a DataTransferObject object - and perhaps vice versa.
If, for some unfathomable reason, you don't want to introduce such a Mapper into your System Under Test, you can still add it as a reusable Service in your test project.
If you don't wish to write such a Mapper yourself, you could consider using something like AutoMapper for that purpose.
Inside an AngularJS directive, I assign a new value to a scope variable:
$scope.myPerson = { TiersId: 105191, Name: "John Smith" };
Originaly the $scope.myPerson was created from a BreezeJS entity.
Assigning the new value triggers a $scope.apply() by AngularJS, which is then intercepted by BreezeJS. That's when it gets complicated.
[EDIT]
Ok, I've figured out that I need to use the EntityManager that I've registered with my dataContext:
$scope.myPerson = myDataContext.createPerson({ TiersId: 105191, Name: "John Smith" });
function createPerson(person) {
return manager.createEntity("AccountOwner", person);
}
Now, it fails in the following code:
proto.createEntity = function (typeName, initialValues, entityState) {
entityState = entityState || EntityState.Added;
var entity = this.metadataStore
._getEntityType(typeName)
.createEntity(initialValues);
if (entityState !== EntityState.Detached) {
this.attachEntity(entity, entityState);
}
return entity;
};
The entity type is known, but the createEntity(initialValues) function is undefined. How come ?
[EDIT]
To make things clearer, here's the relevant EF mapping as well as the model classes:
public class MandateMappings : EntityTypeConfiguration<Mandate>
{
public MandateMappings()
{
Property(m => m.IBAN).HasMaxLength(34).IsFixedLength().IsUnicode(false);
Property(m => m.AccountOwner.Name).HasMaxLength(70);
Property(m => m.AccountOwner.City).HasMaxLength(500);
Property(m => m.CreatedBy).HasMaxLength(30);
Property(m => m.UpdatedBy).HasMaxLength(30);
}
}
public class Mandate : Audit
{
public string IBAN { get; set; }
public AccountOwner AccountOwner { get; set; }
}
public class AccountOwner
{
public string Name { get; set; }
public string City { get; set; }
}
public abstract class Audit
{
public DateTime CreatedDate { get; set; }
public string CreatedBy { get; set; }
}
Let me clarify what I meant when I said on User Voice that Breeze supports a form of inheritance but not "database inheritance".
I meant that, today, the classes on your server-side can be part of an inheritance chain if and only if that chain is invisible to the client.
Here are some conditions consistent with that caveat:
Only the "terminal" class in the chain (the most derived class) maps to a database table.
Properties on super classes are non-public (e.g., internal) or explicitly not mapped (e.g., adorned with [System.ComponentModel.DataAnnotations.Schema.NotMapped].
Methods may appear on any class at any level as these are never transmitted to the client.
Here is an example of a TodoItem class that inherits from a baseClass:
public class baseClass
{
public void DoNothing() {}
internal string Foo { get; set; }
}
public class TodoItem :baseClass
{
public int Id { get; set; }
[Required, StringLength(maximumLength: 30)]
public string Description { get; set; }
public System.DateTime CreatedAt { get; set; }
public bool IsDone { get; set; }
public bool IsArchived { get; set; }
}
This works fine on the server. Set a breakpoint in the controller: you'll have no trouble executing DoNothing() and getting/setting the Foo property.
This works because there is no client-side consequence of this structure. The metadata are no different after deriving from baseClass than they were before. The Foo property and DoNothing methods are invisible to the client … exactly as this service author intended.
This kind of arrangement is pretty common in the real world where the many classes of a business model share functionality through a base class.
This is NOT the end of the story and it is NOT what we think people are asking for when they ask for "inheritance".
We think people want what I have been calling "database inheritance" by which I mean that two or more classes in the inheritance chain are mapped to different tables.
Breeze does not handle that today ... in part because Breeze cannot yet comprehend metadata that describe an inheritance hierarchy.
Workaround
What if you had a class hierarchy in which data properties were defined on different class levels? You can workaround the current obstacles by providing a metadata description that flattens the hierarchy from the perspective of the client.
For example, suppose you have a Person type with FirstName and LastName. And Person derives from entityBase which defines createdBy.
If you define the Person *EntityType* to have [FirstName, LastName, and createdBy] properties - essentially flattening the hierarchy - all will be well.
Flatten the hierarchy automagically
Of course that's a PITA. One approach to inheritance we could take is to do this flattening for you when you ask Breeze to generate the metadata on the server.
I'm curious: would this suffice? Or do you really NEED to know on the JavaScript client that the createdBy property belongs to a base class. If you really need to know, please tell me why.
Edit: As of v 1.3.1 Breeze now DOES support inheritance.
Without more context I can't be sure, but I'm guessing that the issue is that Breeze does not YET have metadata about your entityType. Normally this is accomplished via your first query, but if you are creating entities before the first query then the alternative is to call the EntityManager.fetchMetadata() method instead BEFORE performing any createEntity calls. The fetchMetadata method is asynchonous, i.e. returns a promise, so you will need to perform your createEntity call inside of the 'then' portion of the promise. There are a couple of other recent 'Breeze' posts similar to this that have more details and examples.
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
Say you have an domain entity with business logic for initializing its default values. E.g.
class User : IUser, Entity
{
public User()
{
StartDate = DateTime.Now;
EndDate = StartDate.AddDays(3); // This value could be user-configured.
}
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
Using RIA services, the DTO that will be generated of course does not include any logic, only public properties. Which means that when a client (e.g. Silverlight application) displays a 'create new user' dialog, it will not be able to populate the fields with any default values (without, of course, duplicating the business logic on the client).
In an attempt to achieve this, I created new DTO (UserDTO) and a query in my UserDomainService:
// Construct a new domain entity and then convert to DTO
public UserDTO CreateNewUser()
{
var user = new User(); // Business logic has now been executed.
return new UserDTO(user);
}
This does allow the client to populate fields with default values, however when it comes time to add the newly created user, RIA has already added the UserDTO to it's internally mainted collection of entities, so you cannot call .Add on your domain context. You can still just call SubmitChanges, which will trigger the [Update] method to be called, however this seems to be going against the grain of how RIA services is supposed to be used (i.e. you shouldn't be doing an INSERT operation in an UPDATE method).
Is this scenario (i.e. server-side creation of DTOs) achievable in RIA services?
I don't know what your business logic looks like, but if you used a common method to save objects (whether new or modified) on the server, than you would be able to differentiate within that method, whether it is a modified object or really a new one.
Example on the server:
[Insert]
public void InsertUser(UserDTO user)
{
this.SaveUser(user);
}
[Update]
public void UpdateUser(UserDTO user)
{
this.SaveUser(user);
}
You could add a property to your User (or the base class, if you have one):
public class UserDTO
{
[...]
// only set this within the constructor,
// unfortunately it cannot be "private set", because of RIA Services
public bool IsNewEntity { get; set; }
}
In your SaveUser method use that flag:
private void SaveUser(UserDTO user)
{
if (user.IsNewEntity)
{
// do something with a new user
}
else
{
// do something with an existing user
}
}
The Constructor for the UserDTO would then be:
public UserDTO()
{
this.IsNewEntity = true;
}
I know, this looks a little trivial, but I do not know of a more "elegant" way.