Mormally setting up one-to-many associations is easy. Take for example:
class Author {
String firstName
String lastName
static hasMany = [books: Book]
static constraints = {
books(nullable: true)
}
}
class Book {
String title
Author author
Publisher publisher
static constraints = {
author(nullable: true)
publisher(nullable: true)
}
}
However, if I've already setup the Author domain without knowing Book at all, there is no static hasMany = [books: Book] specified initially. Later, I want to add a Book domain and want to add static hasMany = [books: Book] to Author. Could I do this with a plugin? If so, how?
Thanks.
If you don't want to update the Author class, you could create your own association class.
class AuthorsToBooks {
Author author
static belongsTo = [Book: book]
}
Related
I have a Grails application using Grails 2.3.8 and Mongo GORM plugin 3.0.1 . I have a service which constructs an object during its first invocations and saves it in mongoDB and returns it. In subsequent invocations, it would just retrieve the constructed object from the mongoDB and return it.
def loadWeekData(String startDate,String storeId){
def weekJson = WeekJson.findByStoreIdAndStartDate(storeId,startDate)
if(weekJson==null){
//construct weekJson here
weekJson.save(flush:true)
weekJson=WeekJson.findByStoreIdAndStartDate(storeId,startDate)
}
weekJson
}
WeekJson domain class has other nested objects with hasMany relation. WeekJson hasMany Employee which hasMany Day which hasMany Planned which hasMany Activity
WeekJson domain class
public class WeekJson{
static hasMany = [employees:Employee]
static mapWith = "mongo"
static mapping = {
employees fetch: 'join'
}
String toString()
{
"$employees"
}
}
Employees domain class
public class Employee {
static mapWith = "mongo"
static hasMany = [days:Day]
static mapping = {
days fetch: 'join'
}
String toString()
{
"$days"
}
}
Day domain class
public class Day {
Planned planned;
static mapWith = "mongo"
static constraints = {
planned nullable:true
}
String toString()
{
" plan: $planned "
}
static mapping = { planned lazy:false}
}
Planned domain class
public class Planned {
List<Activity> activities
static hasMany = [activities:Activity]
static mapWith = "mongo"
static mapping = {
activities lazy:false
}
String toString()
{ activities }
}
Activity Domain class
public class Activity {
String inTime;
String outTime;
double duration;
String type;
String desc;
static mapWith = "mongo"
static constraints = {
duration nullable:true
type nullable:true
desc nullable:true
}
String toString()
{
"$inTime to $outTime"
}
}
I have changed fetching behavior to eager in all the classes with hasMany relations.
The first time, all the nested objects are constrcuted properly, saved in mongoDB, and the returned object is correct.
However, for the next call, Activity objects are null. I've verified that the nested objects are still present in mongoDB during this call. Records in the Planned collection have ids to Activity collection records .
When I do,
println weekJson.employees.days.planned.activities
the list of `Activity is printed. However,
println weekJson
gives Activity list null and so does rendering as Json.
Why is GORM not retrieving the nested objects the second time around ?
Is it possible that this a problem of GORM being unable to handle relationships with this level of nesting ?
Maybe you should switch to sub-documents in your domain model.
Btw, if you want to help us help you, post more data on your case: which version of mongo, grails etc. you are using? what your domain classes look like? what do you see in the mongo collections upon saving?
I need some help here. I am trying to make a portion of an EF Query reusable.
var query = from sr in SomeEFRepository.SelectAll()
select new {
KeyValuePivotField1 = (from kvd in sr.KeyValueData
where kvd.KeyName == "FieldName1"
select kvd.Value).FirstOrDefault(),
KeyValuePivotField2 = (from kvd in sr.KeyValueData
where kvd.KeyName == "FieldName2"
select kvd.Value).FirstOrDefault(),
KeyValuePivotField3 = (from kvd in sr.KeyValueData
where kvd.KeyName == "FieldName3"
select kvd.Value).FirstOrDefault()
}
If you look you can see that I am constantly repeating myself with the following code.
(from kvd in sr.KeyValueData
where kvd.KeyName == "SomeFieldName"
select kvd.Value).FirstOrDefault(),
How can I make a method that EF will recognize in order to do something like this?
var query = from sr in SomeEFRepository.SelectAll()
select new {
KeyValuePivotField1 = GetFieldFromKeyValue("FieldName1"),
KeyValuePivotField2 = GetFieldFromKeyValue("FieldName2"),
KeyValuePivotField3 = GetFieldFromKeyValue("FieldName3"),
}
Any suggestions will help. I have been reading about Expression Trees but not sure if
that would be a good approach or even possible.
Note: I am using EF 5.0 with DBContext.
Extension methods seem like a good fit for this. I don't know exactly what you are doing so this is a example
Customer class
public class Customer{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return string.Format("Customer is {0} {1}", this.FirstName, this.LastName);
}
}
CustomerExtensionMethod
//Class MUST be static
public static class CustomerExtensionMethods
{
//Method MUST be static and use this keyword to be an extension method
public static Customer GetByFirstName(this IEnumerable<Customer> source, string value)
{
return source.Where(c => c.FirstName == value).FirstOrDefault();
}
}
Main program to show how it works
var customers = new List<Customer>
{
new Customer { Id = 1, FirstName = "Foo", LastName = "Bar" },
new Customer { Id = 2, FirstName = "Mark", LastName = "Whoknows" },
new Customer { Id = 3, FirstName = "Ronald", LastName = "McDonald" },
};
var userWithFirstNameOfRonald = customers.GetByFirstName("Ronald");
Console.WriteLine(userWithFirstNameOfRonald.ToString());
This should print "Customer Is Ronald McDonald". The important parts are commented. The Class that contains the method should be static and the method needs to be static. Along with that the first parameter of the method needs to be this to signal it is an extension method. This will allow you to call it on the specific type you put for the first parameter.
Found a great answer and article to my troubles.
http://www.codeproject.com/Articles/402594/Black-Art-LINQ-expressions-reuse
A nice NuGet Package LinqExpressionProjection 1.0.0 that is perfect for my problem.
http://nuget.org/packages/LinqExpressionProjection
I have two classes mapped with MongoDB Grails PLUGIN like that:
Class Person:
class Person {
static mapWith="mongo"
String name
String email
static hasMany = [profiles: Profile]
static belongsTo = Profile
String toString(){
return name +" - " + (profiles)
} }
And Class Profile:
class Profile{
static mapWith="mongo"
String abbreviation
String description
static hasMany = [people: Person]
String toString(){
return abbreviation + " - " + description
}}
How can I make a query to return people per profile with finders provided by mongo?
A mongo query may be useful too!
This finder doesn't return anything
def people = Profile.findAllByAbbreviation("example").people
Sorry the english...
class Person {
static mapWith="mongo"
String name
String email
List<Profile> profile= new ArrayList<Profile>();
static embedded = ['profile']
}
I am working on Entity Framework 4.0 POCO and looking for a way to sort my grid on the list page with a field other than the primary key in the table/entity. I do not want to add a custom page though it is one of the solutions as that would be an overhead if this can be done otherwise.
Thanks in advance.
If the result you are getting back is something like IEnumerable< YourPocoClass > or IQueryable< YourPocoClass >, you can always use .OrderBy(p => p.SomePropertyOtherThanPK)
You could just do this :
var list = yourDbContext.YourDbSet.OrderBy(m=>m.Property);
Or if you wanna sort by string comes from the grid form by the client then you may use the Dynamic Linq:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
Solved the issue with the following attribute.
I added a new attribute class for adding the sort column - DefaultSortOrderColumn
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class DefaultSortColumnAttribute : Attribute
{
public DefaultSortColumnAttribute(string sortColumn, bool sortDescending)
{
this.SortColumn = sortColumn;
this.SortDescending = sortDescending;
}
public string SortColumn { get; private set; }
public bool SortDescending { get; private set; }
}
Then using this attribute for the partial entity class, i.e. the metadata class of your entity, for which we need to use the sort column
[DefaultSortColumn("YourSortColumn", false)]
public partial class YourDBEntity
{
}
We can then use this attribute in the PageTemplates\List.aspx to determine if there are any sortcolumns defined and sort with that in Page_Load.
MetaTable table = GridDataSource.GetTable();
if (!IsPostBack && table.Attributes.OfType<DefaultSortColumnAttribute>().Count() > 0)
{
string sortColumn = table.Attributes.OfType<DefaultSortColumnAttribute>().FirstOrDefault().SortColumn;
bool sortDescending = table.Attributes.OfType<DefaultSortColumnAttribute>().FirstOrDefault().SortDescending;
GridView1.Sort(sortColumn, sortDescending ? SortDirection.Descending : SortDirection.Ascending);
}
This attribute can further be extended and set to the properties instead of the class. That way, we would be able to sort on multiple columns.
Thanks to those reponded. Happy coding.
I have tried DataAnnotation as described here, but it does not work for me.
I have a table with the following structure
Table - Category
id int (pk not null)
CategoryName varchar(100) (null)
I already created my edmx file and all.
I have created the Category.cs file also like below.
[MetadataType(typeof(CategoryMetaData))]
public partial class Category
{
}
public class CategoryMetaData
{
[Required(ErrorMessage = "Category Name is required.")]
public object CategoryName;
}
But my validations are not working.
Is there anything I've missed?
I have found that ObjectContext does not work with DataAnnotations. You have to switch to using DbContext, then it works. Download the EF 4.x DbContext T4 file and try it on your model. Not sure why this is true, was hoping an expert would chime in.
UPD
Solution here.
Before validating, you need to manually register the metadata class
==================
I suppose this problem is related to proxy classes, which EF generates for your entities. You can check this easily in runtime: just see GetType().FullName.
If attribute is marked as non-inheritable, it won't be applied in inherited class. And proxy classes derive from entity classes, so non-inheritable attributes are lost.
I'm trying to use DataAnnotations in WebForms project by checking attributes by hand. But neither
System.ComponentModel.DataAnnotations.Validator.TryValidateObject(entity, new ValidationContext(value, null, null), results, true);
nor
PropertyInfo[] properties = value.GetType()
.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
var validationProperties = properties.Select(prop => new
{
Property = prop,
ValidationAttributes = Attribute.GetCustomAttributes(prop, typeof(ValidationAttribute), true)
}).Where(valProp => valProp.ValidationAttributes.Any());
doesn't work.
I've tried these code with simple class not related to EF, and all DataAnnotations attributes were found and checked correctly.
[MetadataType(typeof(TestValidObject_Metadata))]
public class TestValidObject
{
public string IdName { get; set; }
}
public class TestValidObject_Metadata
{
[Required, DisplayName("Id name")]
public object IdName { get; set; }
}
RequiredAttribute's definition is
[AttributeUsageAttribute(AttributeTargets.Property|AttributeTargets.Field|AttributeTargets.Parameter, AllowMultiple = false)]
public class RequiredAttribute : ValidationAttribute
and by default it becomes inheritable attribute. And I don't know why
Attribute.GetCustomAttributes(prop, typeof(ValidationAttribute), true)
// true specifies to also search the ancestors of element for custom attributes.
doesn't catch it.
Any ideas are welcome.
CategoryName in CateogryMetaData should be a property and has the same type as the original property. Try this:
public class CategoryMetaData
{
[Required(ErrorMessage = "Category Name is required.")]
public string CategoryName {get;set;}
}