Recursive procedure in genexus - genexus

I have one table called Cattegorys that have CategoryId in primary key and CategoryParentId (auto-relationship).
I need to return all Category name parent of my data in the property "CategoryParentsName".
In my CategorysSDT i create this property varchar:
CategoryParentsName VARCHAR(500);
My data provider:
CategoriasSDT
{
data
{
order (CategoryName) when &sort = 'CategoryName-desc'
{
CategoryId= CategoryId
CategoryName= CategoriaNome
CategoryParentId = CategoryParentId
CategoryParentsName= CategoryParentsName
}
}
}
In my transaction i create a formula to my property CategoryParentsName that calls this procedure:
&isTrue = true
&isAchou = true
do while (&isTrue)
if &isAchou
&isAchou = false
for each
where CategoriaId = &CategoryParentId
&CategoryParentsName = CategoryName+ " > test"
&isAchou = true
endfor
else
&isTrue = false
endif
enddo
In my property categoryParentsName i'm receiving the correct name of the category parents but the CategoryParentId is returning null.

Related

Get field name which is related to contact from each entity in dynamics crm using c#

I have one function which gets data as per module name related contacts.
Code:
if (modulename == "lead" || modulename == "opportunity")
{
contactfield= "parentcontactid";
}
else if (modulename == "salesorder" || modulename == "quote" || modulename == "incident" || modulename == "invoice")
{
contactfield= = "customerid";
}
else
{
}
quotequery = new QueryExpression()
{
Distinct = false,
EntityName = modulename,
ColumnSet = new ColumnSet(true),
Criteria =
{
Filters =
{
new FilterExpression
{
FilterOperator = LogicalOperator.And,
Conditions =
{
new ConditionExpression("ownerid", ConditionOperator.Equal, userid),
new ConditionExpression(contactfield, ConditionOperator.Equal, lstcredential.Contactid.ToString())
},
}
}
}
};
queryentityCollection = _serviceProxy.RetrieveMultiple(quotequery).Entities;
But for all module there is different different name which is related to contact.for e.g.in lead it is parentcontactid but for invoice it is customerid.
So Is there any solution for fetch attribute name related to contact from entity name?because if else for each entity is not solution as I do in starting.
Please suggest me answer.
If you are looking for a specific contact lookup on a finite set of entities I would hard code it in a name value pair collection similar to what you're doing except maybe a key value pair store:
private Dictionary<String, String> _contactMappings = new Dictionary<String, String>{"lead", "parentcontactid"}, {"incident", "contactid"};
An alternative would be querying metadata to look for contact lookups, and caching the results, but you wouldn't know which is the correct lookup. For example incident has 3 lookups to contact (contactid, responsiblecontactid, and primarycontactid).

Updating Dynamic Columns Issue

I am trying to update values in table emp. Which column to update is dynamic.
public void updateEmployees(List<String> columnDb, List<String> columnValues)
{
var data = ctx.tblEmployee.Where(e => e.Id == empId).Select(e => e).SingleOrDefault();
....
data.columnDb = columnValues; // Pseudo
ctx.tblEmployee.Add(data);
ctx.SaveChanges();
}
How to update columns which are passed dynamically as a parameter?
You can do this with the power of Reflection.
Just iterate through properties of your object and set the value for the properties that you have in your list.
First, let's build a dictionary with property names and values from your parameters to make the value access easier:
var values = columnDb.Zip(columnValues,
(name, value) => new { Name = name, Value = value })
.ToDictionary(x => x.Name, x => x.Value);
Now, iterate through properties and set values:
var data = ctx.tblEmployee.SingleOrDefault(e => e.Id == empId);
foreach(PropertyInfo property in data.GetType().GetProperties())
{
// Check if property should be updated
if(values.ContainsKey(property.Name))
{
var value = values[property.Name];
// Change the type of the value to the type of the property
object converted = Convert.ChangeType(value, property.PropertyType);
// Set the property value
property.SetValue(data,values[property.Name]);
}
}
Of course, the code above assumes that there is a conversion between string and the type of the properties of your data object.

Explicit construction of entity type in query is not allowed [duplicate]

Using Linq commands and Linq To SQL datacontext, Im trying to instance an Entity called "Produccion" from my datacontext in this way:
Demo.View.Data.PRODUCCION pocoProduccion =
(
from m in db.MEDICOXPROMOTORs
join a in db.ATENCIONs on m.cmp equals a.cmp
join e in db.EXAMENXATENCIONs on a.numeroatencion equals e.numeroatencion
join c in db.CITAs on e.numerocita equals c.numerocita
where e.codigo == codigoExamenxAtencion
select new Demo.View.Data.PRODUCCION
{
cmp = a.cmp,
bonificacion = comi,
valorventa = precioEstudio,
codigoestudio = lblCodigoEstudio.Content.ToString(),
codigopaciente = Convert.ToInt32(lblCodigoPaciente.Content.ToString()),
codigoproduccion = Convert.ToInt32(lblNroInforme.Content.ToString()),
codigopromotor = m.codigopromotor,
fecha = Convert.ToDateTime(DateTime.Today.ToShortDateString()),
numeroinforme = Convert.ToInt32(lblNroInforme.Content.ToString()),
revisado = false,
codigozona = (c.codigozona.Value == null ? Convert.ToInt32(c.codigozona) : 0),
codigoclinica = Convert.ToInt32(c.codigoclinica),
codigoclase = e.codigoclase,
}
).FirstOrDefault();
While executing the above code, I'm getting the following error that the stack trace is included:
System.NotSupportedException was caught
Message="The explicit construction of the entity type 'Demo.View.Data.PRODUCCION' in a query is not allowed."
Source="System.Data.Linq"
StackTrace:
en System.Data.Linq.SqlClient.QueryConverter.VisitMemberInit(MemberInitExpression init)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.VisitSelect(Expression sequence, LambdaExpression selector)
en System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.Visit(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.VisitFirst(Expression sequence, LambdaExpression lambda, Boolean isFirst)
en System.Data.Linq.SqlClient.QueryConverter.VisitSequenceOperatorCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitMethodCall(MethodCallExpression mc)
en System.Data.Linq.SqlClient.QueryConverter.VisitInner(Expression node)
en System.Data.Linq.SqlClient.QueryConverter.ConvertOuter(Expression node)
en System.Data.Linq.SqlClient.SqlProvider.BuildQuery(Expression query, SqlNodeAnnotations annotations)
en System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
en System.Data.Linq.DataQuery`1.System.Linq.IQueryProvider.Execute[S](Expression expression)
en System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
en Demo.View.InformeMedico.realizarProduccionInforme(Int32 codigoExamenxAtencion, Double precioEstudio, Int32 comi) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 602
en Demo.View.InformeMedico.UpdateEstadoEstudio(Int32 codigo, Char state) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 591
en Demo.View.InformeMedico.btnGuardar_Click(Object sender, RoutedEventArgs e) en D:\cs_InformeMedico\app\InformeMedico.xaml.cs:línea 683
InnerException:
Is that now allowed in LINQ2SQL?
Entities can be created outside of queries and inserted into the data store using a DataContext. You can then retrieve them using queries. However, you can't create entities as part of a query.
I am finding this limitation to be very annoying, and going against the common trend of not using SELECT * in queries.
Still with c# anonymous types there is a workaround, by fetching the objects into an anonymous type, and then copy it over into the correct type.
For example:
var q = from emp in employees where emp.ID !=0
select new {Name = emp.First + " " + emp.Last, EmployeeId = emp.ID }
var r = q.ToList();
List<User> users = new List<User>(r.Select(new User
{
Name = r.Name,
EmployeeId = r.EmployeeId
}));
And in the case when we deal with a single value (as in the situation described in the question) it is even easier, and we just need to copy directly the values:
var q = from emp in employees where emp.ID !=0
select new { Name = emp.First + " " + emp.Last, EmployeeId = emp.ID }
var r = q.FirstOrDefault();
User user = new User { Name = r.Name, EmployeeId = r.ID };
If the name of the properties match the database columns we can do it even simpler in the query, by doing select
var q = from emp in employees where emp.ID !=0
select new { emp.First, emp.Last, emp.ID }
One might go ahead and write a lambda expression that can copy automatically based on the property name, without needing to specify the values explictly.
Here's another workaround:
Make a class that derives from your LINQ to SQL class. I'm assuming that the L2S class that you want to return is Order:
internal class OrderView : Order { }
Now write the query this way:
var query = from o in db.Order
select new OrderView // instead of Order
{
OrderID = o.OrderID,
OrderDate = o.OrderDate,
// etc.
};
Cast the result back into Order, like this:
return query.Cast<Order>().ToList(); // or .FirstOrDefault()
(or use something more sensible, like BLToolkit / LINQ to DB)
Note: I haven't tested to see if tracking works or not; it works to retrieve data, which is what I needed.
I have found that if you do a .ToList() on the query before trying to contruct new objects it works
I just ran into the same issue.
I found a very easy solution.
var a = att as Attachment;
Func<Culture, AttachmentCulture> make =
c => new AttachmentCulture { Culture = c };
var culs = from c in dc.Cultures
let ac = c.AttachmentCultures.SingleOrDefault(
x => x.Attachment == a)
select ac == null ? make(c) : ac;
return culs;
I construct an anonymous type, use IEnumerable (which preserves deferred execution), and then re-consruct the datacontext object. Both Employee and Manager are datacontext objects:
var q = dc.Employees.Where(p => p.IsManager == 1)
.Select(p => new { Id = p.Id, Name = p.Name })
.AsEnumerable()
.Select(item => new Manager() { Id = item.Id, Name = item.Name });
Within the book "70-515 Web Applications Development with Microsoft .NET Framework 4 - Self paced training kit", page 638 has the following example to output results to a strongly typed object:
IEnumerable<User> users = from emp in employees where emp.ID !=0
select new User
{
Name = emp.First + " " + emp.Last,
EmployeeId = emp.ID
}
Mark Pecks advice appears to contradict this book - however, for me this example still displays the above error as well, leaving me somewhat confused. Is this linked to version differences? Any suggestions welcome.
I found another workaround for the problem that even lets you retain your result as IQueryale, so it doesn't actually execute the query until you want it to be executed (like it would with the ToList() method).
So linq doesn't allow you to create an entity as a part of query? You can shift that task to the database itself and create a function that will grab the data you want. After you import the function to your data context, you just need to set the result type to the one you want.
I found out about this when I had to write a piece of code that would produce a IQueryable<T> in which the items don't actually exist in the table containing T.
pbz posted a work around by creating a View class inherited from an entity class that you could be working with. I'm working with a dbml model of a table that has > 200 columns. When I try and return the whole table I get "Root Element missing" errors. I couldn't find anyone who wanted to deal with my particular issue so I was looking at rewriting my entire approach. Just creating a view class for the entitiy class worked in my case.
As pbz suggests : Create a view class that inherits from your entity class. For me this is tbCamp so :
internal class tbCampView : tbCamp
{
}
Then use the view class in your query :
using (var dc = ConnectionClass.Connect(Dev))
{
var camps = dc.tbCamps.Select(s => new tbCampView
{
active = s.active,
idCamp = s.idCamp,
campName = s.campName
});
SmartTableViewer(camps, dg1);
}
private void SmartTableViewer<T>(IEnumerable<T> allRecords)
{
// Build sorted rows back into new table
var table = new DataTable();
// Create columns based on type
if (allRecords is IEnumerable<tbCamp> tbCampRecords)
{
// Get the columns you want
table.Columns.Add("idCamp");
table.Columns.Add("campName");
foreach (var record in tbCampRecords)
{
// Make a new row
var r = table.NewRow();
// Add the contents to each column of the row
r["idCamp"] = record.idCamp;
r["campName"] = record.campName;
// Add the row to the table.
table.Rows.Add(r);
}
}
else
{
MessageBox.Show("Unhandled type. Add support for new data type in SmartTableViewer()");
return;
}
// Update table in grid
dg1.DataSource = table.DefaultView;
}
Here is what happens when you try and create an entity class object in the query.
I didn't want to have to use an anonymous type if I could help it because I wanted the type to be tbCamp. Since tbCampView is of type tbCamp the is operator works well. see Brian Hasden's answer Passing a generic List<> in C#
I'm surprised this is even an issue but with larger tables I run into this error so I thought I would just show it here :
When trying to read this table into memory I get the following error. There are < 2000 rows but the columns are > 200 for each. I don't know if that is an issue or not.
If I just want a few columns I need to create a custom class and handle that which isn't that big of a pain. With the approach pbz provided I don't have to worry about it.
Here is the entire project in case it helps someone.
public partial class Form1 : Form
{
private const bool Dev = true;
public Form1()
{
InitializeComponent();
}
private void btnGetAllCamps_Click(object sender, EventArgs e)
{
using (var dc = ConnectionClass.Connect(Dev))
{
IQueryable<tbCampView> camps = dc.tbCamps.Select(s => new tbCampView
{
// Project columns as needed.
active = s.active,
idCamp = s.idCamp,
campName = s.campName
});
// pass in as a
SmartTableViewer(camps);
}
}
private void SmartTableViewer<T>(IEnumerable<T> allRecords)
{
// Build sorted rows back into new table
var table = new DataTable();
// Create columns based on type
if (allRecords is IEnumerable<tbCamp> tbCampRecords)
{
// Get the columns you want
table.Columns.Add("idCamp");
table.Columns.Add("campName");
foreach (var record in tbCampRecords)
{
//var newRecord = record;
// Make a new row
var r = table.NewRow();
// Add the contents to each column of the row
r["idCamp"] = record.idCamp;
r["campName"] = record.campName;
// Add the row to the table.
table.Rows.Add(r);
}
}
else
{
MessageBox.Show("Unhandled type. Add support for new data type in SmartTableViewer()");
return;
}
// Update table in grid
dg1.DataSource = table.DefaultView;
}
internal class tbCampView : tbCamp
{
}
}

Only primitive types ('such as Int32, String, and Guid') are supported in this context when I try updating my viewmodel

I am having some trouble with a linq query I am trying to write.
I am trying to use the repository pattern without to much luck. Basically I have a list of transactions and a 2nd list which contains the description field that maps against a field in my case StoreItemID
public static IList<TransactionViewModel> All()
{
var result = (IList<TransactionViewModel>)HttpContext.Current.Session["Transactions"];
if (result == null)
{
var rewardTypes = BusinessItemRepository.GetItemTypes(StoreID);
HttpContext.Current.Session["Transactions"] =
result =
(from item in new MyEntities().TransactionEntries
select new TransactionViewModel()
{
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID).ItemDescription,
TransactionDate = item.PurchaseDate.Value,
TransactionAmount = item.TransactionAmount.Value,
}).ToList();
}
return result;
}
public static List<BusinessItemViewModel>GetItemTypes(int storeID)
{
var result = (List<BusinessItemViewModel>)HttpContext.Current.Session["ItemTypes"];
if (result == null)
{
HttpContext.Current.Session["ItemTypes"] = result =
(from items in new MyEntities().StoreItems
where items.IsDeleted == false && items.StoreID == storeID
select new BusinessItemViewModel()
{
ItemDescription = items.Description,
StoreID = items.StoreID,
StoreItemID = items.StoreItemID
}).ToList();
}
return result;
However I get this error
Unable to create a constant value of type 'MyMVC.ViewModels.BusinessItemViewModel'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
I know its this line of code as if I comment it out it works ok
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID).ItemDescription,
How can I map ItemDescription against my list of itemTypes?
Any help would be great :)
This line has a problem:
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID)
.ItemDescription,
Since you are using FirstOrDefault you will get null as default value for a reference type if there is no item that satifies the condition, then you'd get an exception when trying to access ItemDescription - either use First() if there always will be at least one match or check and define a default property value for ItemDescription to use if there is none:
ItemDescription = itemTypes.Any(r=>r.StoreItemID==item.StoreItemID)
? itemTypes.First(r=>r.StoreItemID==item.StoreItemID)
.ItemDescription
: "My Default",
If itemTypes is IEnumerable then it can't be used in your query (which is what the error message is telling you), because the query provider doesn't know what to do with it. So assuming the that itemTypes is based on a table in the same db as TransactionEntities, then you can use a join to achieve the same goal:
using (var entities = new MyEntities())
{
HttpContext.Current.Session["Transactions"] = result =
(from item in new entities.TransactionEntries
join itemType in entities.ItemTypes on item.StoreItemID equals itemType.StoreItemID
select new TransactionViewModel()
{
ItemDescription = itemType.ItemDescription,
TransactionDate = item.PurchaseDate.Value,
TransactionAmount = item.TransactionAmount.Value,
CustomerName = rewards.CardID//TODO: Get customer name
}).ToList();
}
I don't know the structure of your database, but hopefully you get the idea.
I had this error due a nullable integer in my LINQ query.
Adding a check within my query it solved my problem.
query with problem:
var x = entities.MyObjects.FirstOrDefault(s => s.Obj_Id.Equals(y.OBJ_ID));
query with problem solved:
var x = entities.MyObjects.FirstOrDefault(s => s.Obj_Id.HasValue && s.Obj_Id.Value.Equals(y.OBJ_ID));

Error when creating multiple N:1 relationships for an entity

I am using the MetadataService to create 4 entities and create relationships between them.
The entities are: TrexCalendar, TrexFrom, TrexTo, TrexAddress
The relationships (all 1:Many) are:TrexFrom - TrexAddress, TrexCalendar - TrexFrom, TrexTo - TrexAddress, TrexCalendar - TrexTo
When I run my code the entities are all created successfully and the first, second and fourth relationships are created successfully.
Creating the third relationship fails with the following details:
0x80047007 Entity: new_trexaddress is parented to Entity with id:
7a6af338-bc23-e011-ad8c-9f5d300a22fe. Cannot create another parental
relation with Entity: new_trexto Platform
7a6af338-bc23-e011-ad8c-9f5d300a22fe is the id for the TrexFrom entity.
So it looks like the SDK won't allow me to create a 1:N relationship between TrexTo and TrexAddress because a 1:N relationship exists between TrexFrom and TrexAddress.
What's weird is that I am able to create this relationship manually using the Dynamics web interface.
Any ideas what might be going on? How can I create both relationships programmatically?
I'm using the following code to create the relationships:
OneToManyMetadata relationship = new OneToManyMetadata
{
ReferencedEntity = "new_trexto",
ReferencingEntity = "new_trexaddress"
SchemaName = "new_trexto_trexaddress",
AssociatedMenuBehavior = new CrmAssociatedMenuBehavior { Value = AssociatedMenuBehavior.UseCollectionName },
CascadeAssign = new CrmCascadeType { Value = CascadeType.NoCascade },
CascadeDelete = new CrmCascadeType { Value = CascadeType.RemoveLink },
CascadeMerge = new CrmCascadeType { Value = CascadeType.NoCascade },
CascadeReparent = new CrmCascadeType { Value = CascadeType.NoCascade },
CascadeShare = new CrmCascadeType { Value = CascadeType.UserOwned },
CascadeUnshare = new CrmCascadeType { Value = CascadeType.NoCascade }
};
LookupAttributeMetadata lookup = new LookupAttributeMetadata
{
SchemaName = lookupName,
RequiredLevel = new CrmAttributeRequiredLevel(AttributeRequiredLevel.Recommended),
DisplayName = CrmServiceUtility.CreateSingleLabel("TrexTo - TrexAddress", 1033)
};
CreateOneToManyRequest request = new CreateOneToManyRequest
{
OneToManyRelationship = relationship,
Lookup = lookup
};
try
{
metadataService.Execute(request);
Debug.Print("Relationship created successfully");
}
catch (System.Web.Services.Protocols.SoapException ex)
{
Debug.Print(ex.Detail.InnerText);
}
If both relationships are set as "Parental", you'll probably need to set one of them as "Referential"

Resources