Breeze Validate Entity and its navigational properties - validation

I am able to validate an entity using the code entity.entityAspect.validateEntity(). However, this does not validate navigation properties. My entiy has one-to-one and one-to-many relationship with out entities. I want to validate both the entity and its navigational properties. How can i do this with breeze?
EDIT
I have a class
public class ClassA{
public int id{get; set;}
public List<ClassB> navigationArray{get; set;}
}
public class ClassB{
public int myClass {get; set;}
[Foreign("myClass")]
public ClassA ClassA_E{get; set;}
}
I add an object O1 of ClassA to the entity manager; and add an object O2 of classB to the entity manager and set the property, ClassA_E to O1. All works well but when validating O1, O2 does not get validated

EntityAspect.validateEntity WILL validate navigation properties ( The code below was tested in breeze 1.4.17).
You can add your own validators to any navigation property: In the examples below assume a schema with "Customer" and "Order" entity types where each Customer has an nonscalar "orders" property and each "Order" has a scalar "customer" property.
In this case, the scalar "customer" navigation property on the Order type might have a validator registered like this:
var orderType = em.metadataStore.getEntityType("Order");
var custProp = orderType.getProperty("customer");
// validator that insures that you can only have customers located in 'Oakland'
var valFn = function (v) {
// v is a customer object
if (v == null) return true;
var city = v.getProperty("city");
return city === "Oakland";
};
var customerValidator = new Validator("customerValidator", valFn, { messageTemplate: "This customer's must be located in Oakland" });
custProp.validators.push(customerValidator);
where the validation error would be created by calling
myOrder.entityAspect.validateEntity();
And the nonscalar navigation property "orders" on the "Customer" type might have a validator registered like this:
var customerType = em.metadataStore.getEntityType("Customer");
var ordersProp = customerType.getProperty("orders");
// create a validator that insures that all orders on a customer have a freight cost > $100
var valFn = function (v) {
// v will be a list of orders
if (v.length == 0) return true; // ok if no orders
return v.every(function(order) {
var freight = order.getProperty("freight");
return freight > 100;
});
};
var ordersValidator = new Validator("ordersValidator", valFn, { messageTemplate: "All of the orders for this customer must have a freight cost > 100" });
ordersProp.validators.push(ordersValidator);
where the validation error would be created by calling
myCustomer.entityAspect.validateEntity();

Related

Validate entity's property value without unwanted dependancies

I have an "Customer" entity with several properties, one of them is "countryCode". When the country code gets set, it should be checked if it is valid. I could use the setter of Customer to do this validation but the problem is, there is a table "country" which holds the available country codes and as far as I know it is considered bad style to make an entity depended on any repositories.
I could let a service do the validation:
class CustomerUpdateService{
public function updateFromDto(Customer $customer, CustomerUpdateDto $updateDto): Customer
{
$countryCode = $updateDto->getCountryCode();
$country = $this->countryRepository->find($countryCode);
if (!isset($country){
throw new InvalidArgumentException('Unallowed country code ' . $countryCode);
}
$customer->setCountryCode($countryCode);
//update other properties
return $customer;
}
}
In my opion an entity should not allow to set invalid property states, so it has to to the validation by its own. But how to achieve this without generating unwanted dependencies? The only thing I can think of is passing the allowed countries to the setter:
class Customer{
public function setCountryCode(string $countryCode, array $availableCountries){
$availableCountryCodes = [];
foreach ($availableCountries as $country){
$availableCountryCodes[] = $country->getCode();
}
if (!in_array($countryCode, $availableCountryCodes)){
throw new InvalidArgumentException('Unallowed country code ' . $countryCode);
}
$this->countryCode = $countryCode;
}
}
Or passing a CountryCodeValidator to the setter:
class Customer{
public function setCountryCode(string $countryCode, CountryCodeValidator $validator){
$validator->validate($countryCode);
$this->countryCode = $countryCode;
}
}
Is this a legitimate solution? Are there better approaches?

Expression.Property(param, field) is "trolling" me [System.ArgumentException] = {"Instance property 'B.Name' is not defined for type A"}

Once again, I am facing an issue, this time with LINQ Expression builder and this time I am even struggling to find the reason why it's not working. I have a Database-First EF project with quite a few tables. For this specific case, I have to use 2 of them - DocHead and Contragent. MyService.metadata.cs looks like this:
[MetadataTypeAttribute(typeof(DocHead.DocHeadMetadata))]
public partial class DocHead
{
// This class allows you to attach custom attributes to properties
// of the DocHead class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class DocHeadMetadata
{
// Metadata classes are not meant to be instantiated.
private DocHeadMetadata()
{
}
public string doc_Code { get; set; }
public string doc_Name { get; set; }
public string doc_ContrCode { get; set; }
//...
[Include]
public Contragent Contragent { get; set; }
}
}
[MetadataTypeAttribute(typeof(Contragent.ContragentMetadata))]
public partial class Contragent
{
// This class allows you to attach custom attributes to properties
// of the Contragent class.
//
// For example, the following marks the Xyz property as a
// required property and specifies the format for valid values:
// [Required]
// [RegularExpression("[A-Z][A-Za-z0-9]*")]
// [StringLength(32)]
// public string Xyz { get; set; }
internal sealed class ContragentMetadata
{
// Metadata classes are not meant to be instantiated.
private ContragentMetadata()
{
}
public string Code { get; set; }
public string Name { get; set; }
//...
I take some docHeads like this:
IQueryable<DocHead> docHeads = new MyEntities().DocHead;
Then I try to sort them like this:
docHeads = docHeads.OrderByDescending(x => x.Contragent.Name);
It is all working like I want it. I get those docHeads sorted by the name of the joined Contragent. My problem is that I will have to sort them by a field, given as a string parameter. I need to be able to write something like this:
string field = "Contragent.Name";
string linq = "docHeads = docHeads.OrderByDescending(x => x." + field + ")";
IQueryable<DocHead> result = TheBestLinqLibraryInTheWorld.PrepareLinqQueryable(linq);
Unfortunately, TheBestLinqLibraryInTheWorld does not exist (for now). So, I have set up a method as a workaround.
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "x");
var prop = Expression.Property(param, SortField); // normally returns x.sortField
var exp = Expression.Lambda(prop, param); // normally returns x => x.sortField
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp); // normally returns sth similar to q.OrderBy(x => x.sortField)
return q.Provider.CreateQuery<T>(mce);
}
Normally... yes, when it comes to own properties of the class DocHead - those prefixed with doc_. The disaster strikes when I call this method like this:
docHeads = docHeads.OrderByField<DocHead>("Contragent.Name", true); // true - let it be Ascending order
To be more specific, the exception in the title is thrown on line 2 of the method OrderByField():
var prop = Expression.Property(param, SortField);
In My.edmx (the model), the tables DocHead and Contragent have got a relation already set up for me, which is the following: 0..1 to *.
Once again, I have no problem writing "static" queries at all. I have no problem creating "dynamic" ones via the method OrderByField(), but only when it comes to properties of the class DocHead. When I try to order by a prop of the joined Contragent class - the disaster strikes. Any help will be greatly appretiated, thank you!
The problem is that Expression.Property method does not support nested properties. It does exactly what it says - creates expression that represents a property denoted by propertyName parameter of the object denoted by the expression parameter.
Luckily it can easily be extended. You can use the following simple Split / Aggregate trick anytime you need to create a nested property access expression:
var prop = SortField.Split('.').Aggregate((Expression)param, Expression.Property);

how to select value by default in dropdown list when use viewdata["Country"] and get get list from database

i have country list stored in database and using viewdata to store all list now when i edit my task then i want to set my value in dropdown list. my code is
public ActionResult Edit(long EventId)
{
using (Event objEvent = new Event())
{
List<EventObject> lst = new List<EventObject>();
lst = objEvent.GetEventByEventId(EventId);
using (Country objContry = new Country())
{
ViewData["Country"] = new SelectList(objContry.GetAllCountry(), "Country", "Country");
}
return View(lst[0]);
}
}
at lst[0].Country is my a value which i want to selected by default in dropdownlist.
my view is
<h5>Country</h5>
#Html.DropDownListFor(model => model.Country, (SelectList)ViewData["Country"], new { id = "ddlCountry" })
You seem to be binding the dropdown to a complex property on your model (Country) which obviously is not supported. Dropdowns should be bound only to simple scalar type properties. So you should define a property that will hold the selected value on your EventObject view model:
public string SelectedCountry { get; set; }
Then in your controller action you should set this property to the value of the country you want to be preselected:
using (Country objContry = new Country())
{
ViewData["Countries"] = new SelectList(objContry.GetAllCountry(), "Country", "Country");
}
lst[0].SelectedCountry = "Argentina";
return View(lst[0]);
and in your view:
#Html.DropDownListFor(
model => model.SelectedCountry,
(SelectList)ViewData["Country"],
new { id = "ddlCountry" }
)
In case your Country property is a scalar type you could directly assign a value to it:
lst[0].Country = "Argentina";

LINQ2SQL Entities - Updating only the fields that have changed

I was hoping there was an easier way to do this in my MVC 3 project. In my database, I have a Customer table that is mapped in my application via LINQ2SQL. There is also a partial customer class where I perform updates, look-up etc - which where I have an update method like this:
public static void Update(Customer customer)
{
if (customer == null)
return;
using(var db = new DataBaseContext)
{
var newCustomer = db.Customers.Where(c => c.customer_id = customer.customer_id).SingleOrDefault();
if(newCustomer == null)
return;
newCustomer.first_nm = customer.first_nm;
// ...
// ... Lot's of fields to update
// ...
newCustomer.phone_num = customer.phone_nm;
db.SubmitChanges();
}
}
What I was hoping to find was a less-cumbersome method to update the fields in newCustomer with the corresponding fields in customer that are different.
Any suggestions? Thanks.
I think you can implement IEqualityComparer:
public class Customer
{
public string first_nm { get; set; }
public int phone_num { get; set; }
}
class CustomerComparer : IEqualityComparer<Customer>
{
public bool Equals(Customer x, Customer y)
{
//Check whether the compared objects reference the same data.
if (Object.ReferenceEquals(x, y)) return true;
//Check whether any of the compared objects is null.
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
//Check whether the customer' properties are equal.
return x.first_nm == y.first_nm && x.phone_num == y.phone_num ;
}
}
and do it as follows:
if (newCustomer != customer)
{
myDbContext.Customers.Attach(customer,true); // true means modified.
}
Or implement ICloneable and set newCustomer to customer.Clone(). then there's no need to attach customer since newCustomer is already attached.
in EF(4.1), I think You just have to attach the entity as modified:
myDbContext.Customers.AttachAsModified(customer, this.ChangeSet.GetOriginal(customer), myContext);
UPDATE:
Well, it seems like L2S needs original values of the entity. In reply to your comment, you have a couple choices: Using a timestamp column, returning a subset of entities, or having the original entity in your hand. In your scenario, you have the original entity already:
// This is your original entity
var newCustomer = db.Customers.Where(c => c.customer_id = customer.customer_id).SingleOrDefault();
So you will most probably can do:
if (customer != newCustomer)
{
myDbContext.Customers.Attach(customer, newCustomer);
}
Note: I'd rename newCustomer to originalCustomer if I were you since it's more related to the entity's state.
The problem with this approach is that you have an extra trip to database to get your original customer (newCustomer in your code). Take a look at here, here and definitely here to see how you can use TimeStamp columns to prevent the extra database trip.

trouble in converting Generic List coming from a WCF Service to a DataTable

I am confused on how can I use generic methods to parse generic list into datatable/dataset. My setup:
1. I have a class Customers defined in WCF Service Library.
namespace Wcf.Sample.ServiceLibrary
{
public class Customers
{
public string ID = string.Empty;
public string CompanyName = string.Empty;
public string ContactName = string.Empty;
}
}
2. I use this class to return a generic list from my OperationContract.
namespace Wcf.Sample.ServiceLibrary
{
[ServiceContract]
public interface ICustomerService
{
[OperationContract]
List<Customers> GetAllCustomers();
}
}
3. Consume WCF Service in web client page. On button click I populate the GridView with the list returned from GetAllCustomers(). This works perfectly fine.
GridView1.DataSource = client.GetAllCustomers();
GridView1.DataBind();
4. Now the issue is, for some reason (sort/paging function) I want to actually convert the returned generic list into a datatable. To do so, I have a method that returns me a datatable which I want to bind to a GridView. Here are the methods:
public static DataTable ConvertTo<T>(System.Collections.Generic.List<T> genericList)
{
//create DataTable Structure
DataTable dataTable = CreateTable<T>();
Type entType = typeof(T);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
//get the list item and add into the list
foreach (T item in genericList)
{
DataRow row = dataTable.NewRow();
foreach (PropertyDescriptor prop in properties)
{
row[prop.Name] = prop.GetValue(item);
}
dataTable.Rows.Add(row);
}
return dataTable;
}
public static DataTable CreateTable<T>()
{
//T –> ClassName
Type entType = typeof(T);
//set the datatable name as class name
DataTable dataTable = new DataTable(entType.Name);
//get the property list
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
foreach (PropertyDescriptor prop in properties)
{
//add property as column
dataTable.Columns.Add(prop.Name, prop.PropertyType);
}
return dataTable;
}
I am not sure how to call this function? How can I specify the as Customers class which is actually in a webservice? Totally lost. I would appreciate if someone can guide me on the following code, how to make it work.
GridView1.DataSource = ConvertTo<???>(client.GetAllCustomers());
I was able to resolve this issue by modifing the WCF Service itself (although I was reluctant to do so). I modified the GetAllCustomers method to return a datatable instead of generic type. In the service itself, I am converting the generic type into datatable using the same methods:
public static DataTable ConvertTo<T>(System.Collections.Generic.List<T> genericList)
{
//create DataTable Structure
DataTable dataTable = CreateTable<T>();
Type entType = typeof(T);
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
//get the list item and add into the list
foreach (T item in genericList)
{
DataRow row = dataTable.NewRow();
foreach (PropertyDescriptor prop in properties)
{
row[prop.Name] = prop.GetValue(item);
}
dataTable.Rows.Add(row);
}
return dataTable;
}
public static DataTable CreateTable<T>()
{
//T –> ClassName
Type entType = typeof(T);
//set the datatable name as class name
DataTable dataTable = new DataTable(entType.Name);
//get the property list
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
foreach (PropertyDescriptor prop in properties)
{
//add property as column
dataTable.Columns.Add(prop.Name, prop.PropertyType);
}
return dataTable;
}
Another thing that I noticed is that the following line
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
would always returned null for my type. This was due to the fact that I didn't have any get/set methods in Customers class. I created get/set methods in Customer class and everything worked like a charm.
Thanks to everyone who helped and those who tried to help :)

Resources