I'm working on a custom web service for a CRM 2011 deployment, and I'm now making an overview which relates to some of our entities. We have a custom product entity with details such as the product name and category, and we have an agreement entity, which links the product to the customer and contains product details specific to that customer. agreement and product have an N:1 relationship.
I'm now making an overview page that will be displayed on the customer page in CRM, which should display all agreements on that customer. For this display, I also need to retrieve some information about the products these agreements link to, which will be used to group the agreements on the overview page. I've been unable to find any relevant/specific examples on this, and I'm stuck trying to find a feasible way of querying for the data I need.
The way I imagine it, I would like to use two queries to get the required data. First, a query that gets all the agreements on the customer. Then pass this list to a second query, which returns all the products that intersect the first list. Is this possible using a QueryExpression, or do I need to loop over the agreement list and run a separate query for each individual association? I'd prefer to avoid FetchXML if possible.
If I understand your question right, you'll just need to add a LinkEntity to your Product entity on the first query. You can also specify attributes of the Product entity to return as well, but they get returned as AliasedValues, so be aware of that.
Adding to Daryls answer, you can see this post for an example on how to link entities. MSDN is confusing on the subject (at least to me).
I'm proposing a nice structure of query expression there, while Daryl presents a more compact equivalent. (Mine is of course better. :D )
I looked at LinkEntity, but I eventually achieved what I wanted to do with ConditionOperator.In on the second query, like this:
query.Criteria.AddCondition(
new ConditionExpression(
"myprefix_productid",
ConditionOperator.In,
agreementList )
);
Where agreementList is an array of Guids. I tried this before posting the question as well, but without wrapping the parameters in new ConditionExpression(), which didn't work. Presumably this was because the implicit way invoked the wrong overload for the AddCondition function.
Useful links in the other answers though, I'll look into them as well!
Related
I was wondering if it was possible to select/build a multilevel object hierarchy in one single query? As I'm not sure about the terminology, I'll give an example:
Let's say we have a Product, which has one Subrange, which has one Range, which has one Provider.
I can easily build my Product POCO with its Subrange with
Db.Products.WithSubrange.Get(#id);
But could I build it with Product.Subrange.Range and Product.Subrange.Range.Provider in a single query?
I've tried several ways, like:
Db.Products.With(Db.Products.Subrange.WithRange()).Get(#id);
Db.Products.With(Db.Subrange.WithRange()).Get(#id);
but I can't find it out. It would be ok to query the Range and Provider afterward (it's already amazing this way) but a single query would be nice. Explicit Joins maybe?
BTW if Sir Rendle happens to come-by, I would like to thank him for his amazing work. Simple.Data rocks!
Try chaining the 'With's
Db.Products.WithSubRange().WithRange().WithProvider().Get(#id);
Alternatively you can use
Db.Products.FindAllById(#id).WithSubRange().WithRange().WithProvider().FirstOrDefault();
Read: http://simplefx.org/simpledata/docs/pages/Retrieve/LazyVsEagerLoading.htm
The act of doing what you have described relates to the terms "Lazy Loading" and "Eager Loading".
I am working on a CMS using ASP.NET / C#. It works fine and life is good. However, it was decided to add extra functionality to support a wider variety of websites.
Basically, the current CMS is still alpha but will be able to host data for multiple websites running for the same group of companies all having somewhat the same requirements.
I have constructed the database as per the diagram attached below:
"Image removed for security reasons"
Now, in addition to this diagram, I was asked to add support for products under different 'attributes' than Sectors. Meaning that a product now can have a type and many other attributes. So let's say you're using one of our sites and we make tissue paper. Products should somehow support filtering by 'type' say 'tissue' and by 'application' say 'how you use it' and other unknown 'attributes' that may pop up in the future.
For example a product may fall under the 'Agricultural Packaging' sector and is of type 'Bag' and is applied by some way of applying it.
The end result is to be able to sort products by how they are used and or their type and or the sector they fall under. Or even all together.
What is the best approach for this sort of problem to include into the data model and the CMS?
Thanks!
It looks like you're taking the right approach. If a product can be assigned more than one of the new 'attributes', then you'll want to create a new Attributes table where you can define a finite set of attributes. Then link that table to Products via a cross-reference table, just like you're doing with Products & Sectors. If a Product can only be assigned a singe attribute, then add the Attributes table, but instead of using a cross-reference table, add an AttributeID column to the Products table to join the two.
I feel like I may be telling you something that you already know, however. So if there is any additional information, or more specifics you want, maybe you can post an update.
The pattern you are looking for is called EAV
You will end up with an attribute table and an attribute value table that has the product id, attribute id, and attribute value in in it.
This may sound like a pipe dream, I'm wondering if it's possible. I want to be able to take a C# dynamic object, called info, and persist it to a database (I'm currently on a SQL Server 2008 database).
The info object, being dynamic, could have any number of properties: Id, Title, Content, DateExpires, DateAdded, Dateupdated, TypeOf, etc...
Each instance of it could/will contain differing number of properties, depending on what the instance is used for: blog post, classified ad, event, etc... However, there would be a core set of properties every info object would share: Id, MemberId, TypeOf...
The idea is, to have a central table which stores all dynamic info objects, yet, allow me to query based on any property (which may not exist for some objects).
For example, blog posts. They'd have: Id, MemberId, DateAdded, Title, Content, TypeOf, etc... An event would have: Id, MemberId, Title, Content, TypeOf, DateOf, Recurrance, MinAge, MaxAge, etc...
I'd like to build queries based on any given info object property.
Why? Flexibility. If I can get this working, I can use the info object for future cases within my web app. If this is an extremely bad idea, please let me know (and why) please. Thanks!
This is possible and I've seen many systems built like this...however those systems are usually the hardest to maintain due to this "generic nature". There is nothing inherently wrong with this approach. It's just that it's much harder to pull it off and in most instances it ends up being a poorly implemented.
In recent years non-relational databases (like document databases that #Marc Gravell mentioned) have caught up and they are very good for some domains but you need to make sure it's the right fit for your project.
When you take the path of building this "generic database" you are sacrificing other well-known technologies that we take for granted. For example database optimization in relational databases is well-known and there are many tools that work well with them with little or no effort. If you go a different path all of a sudden the tools that you are used to might not work and will end up either building your own to make up for the stuff that does not work (or buying/choosing some esoteric tools.)
Depending on the size of your project it might be wise to build one or two of those systems that you think would be common and then try to see if they are as common as you think.
you could use a 'base' table for the common properties, and a property name-value table for the other properties. meaning:
Table Info
int Id (PK) (FK),
int MemberId,
Date DateAdded //etc...
Table Properties
int InfoId (PK),
varchar PropertyName (PK),
varchar PropertyValue,
string PropertyType //optionaly store information about the type of property
after querying, you can use reflection to translate the properties from (name,value) pairs into proper properties.
That said, I think that this is a very bad idea, for several reasons:
1. this creates further complexity on your CRUD logic
2. you don't have well-defined entities in your domain model, which I don't like
3. validation is that much more difficult- you have to manually verify that Post, for example, doesn't have a property called Recurrance field.
I would use this method only if you truly need this flexibility- for example: if a user can choose to save custom properties that you don't know in advance.
otherwise, if you know that your entities are limited to Post, Event, Employee etc.., I would just limit myself to that.
I have two entities in CRM 2011 - EmailMatchingRule and EmailMatchingRuleField, in a standard parent-child relationship. What I want to retrieve is a set of all of the rules, each with all of its fields pre-fetched as related entities.
Is this even possible? I can get a flattened list using the QueryExpression AddLinkEntity functionality, but that's not really what I'm after.
Using early bound entities and Linq, I can only figure out how to get a list of each, but without the related items.
Any thoughts ?
TIA
Your linq statement needs to use an "Include".
from rule in EmailMatchingRule.Include("EmailMatchingRuleField")
select rule
The collection of rules you'll get back will have all the matching rules now already eagerly loaded.
I inherited an application that uses llblgen 2.6. I have a PersonAppointmentType entity that has a AppointmentType property (n:1 relation). Now I want to sort a collection of PersonAppointmentTypes on the name of the AppointmentType. I tried this so far in the Page_Load:
if (!Page.IsPostBack)
{
var p = new PrefetchPath(EntityType.PersonAppointmentTypeEntity);
p.Add(PersonAppointmentTypeEntity.PrefetchPathAppointmentType);
dsItems.PrefetchPathToUse = p;
// dsItems.SorterToUse = new SortExpression(new SortClause(PersonAppointmentTypeFields.StartDate, SortOperator.Ascending)); // This works
dsItems.SorterToUse = new SortExpression(new SortClause(AppointmentTypeFields.Name, SortOperator.Ascending));
}
I'm probably just not getting it.
EDIT:
Phil put me on the right track, this works:
if (!Page.IsPostBack)
{
dsItems.RelationsToUse = new RelationCollection(PersonAppointmentTypeEntity.Relations.AppointmentTypeEntityUsingAppointmentTypeId);
dsItems.SorterToUse = new SortExpression(new SortClause(AppointmentTypeFields.Name, SortOperator.Ascending));
}
You'll need to share more code if you want an exact solution. You didn't post the code where you actually fetch the entity (or collection). This may not seem relevant but it (probably) is, as I'm guessing you are making a common mistake that people make with prefetch paths when they are first trying to sort or filter on a related entity.
You have a prefetch path from PersonAppointmentType (PAT) to AppointType (AT). This basically tells the framework to fetch PATs as one query, then after that query completes, to fetch ATs based on the results of the PAT query. LLBLGen takes care of all of this for you, and wires the objects together once the queries have completed.
What you are trying to do is sort the first query by the entity you are fetching in the second query. If you think in SQL terms, you need a join from PAT=>AT in the first query. To achieve this, you need to add a relation (join) via a RelationPredicateBucket and pass that as part of your fetch call.
It may seem counter-intuitive at first, but relations and prefetch paths are completely unrelated (although you can use them together). You may not even need the prefetch path at all; It may be that you ONLY need the relation and sort clause added to your fetch code (depending on whether you actually want the AT Entity in your graph, vs. the ability to sort by its fields).
There is a very good explanation of Prefetch Paths and how they were here:
http://www.llblgening.com/archive/2009/10/prefetchpaths-in-depth/
Post the remainder of your fetch code and I may be able to give you a more exact answer.