Custom Selector Attribute - 'Customer' Cannot be found in the system - c#

I made a custom selector that only displays the customers of the current user but when I select a customer I get the error: 'Customer' Cannot be found in the system.
The code for the Custom selector and how I implemented it on the DAC:
[PXNonInstantiatedExtension]
public class SO_SOOrder_ExistingColumn : PXCacheExtension<PX.Objects.SO.SOOrder>
{
#region CustomerID
[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXForeignReference(typeof(Field<SOOrder.customerID>.IsRelatedTo<BAccount.bAccountID>))]
[SalesRepCustomer]
public int? CustomerID { get; set; }
#endregion
}
public class SalesRepCustomer : PXCustomSelectorAttribute
{
public SalesRepCustomer() : base(typeof(Customer.acctCD))
{
this.DescriptionField = typeof(Customer.acctCD);
}
protected virtual IEnumerable GetRecords()
{
foreach (Customer pc in PXSelect<Customer>.Select(this._Graph))
{
//Getting Current UserID
var cache1 = _Graph.Caches[BqlCommand.GetItemType(typeof(AccessInfo.userName))];
AccessInfo currentCacheObjecta = (AccessInfo)cache1.Current;
var userName = currentCacheObjecta.UserName;
SalesPerson person = PXSelect<SalesPerson, Where<SalesPerson.descr, Equal<Required<AccessInfo.userName>>>>.Select(_Graph, userName);
if (person != null)
{
CustSalesPeople custSalesPeople = PXSelect<CustSalesPeople, Where<CustSalesPeople.salesPersonID, Equal<Required<SalesPerson.salesPersonID>>, And<CustSalesPeople.bAccountID, Equal<Required<CustSalesPeople.bAccountID>>>>>.Select(_Graph, person.SalesPersonID, pc.BAccountID);
//return all customers related to this SalesPersonID in the CustSalesPeople table
if (!(custSalesPeople is null))
{
yield return pc;
}
}
else
{
//current user is not a sales person
//return all of the customers
yield return pc;
}
}
}
}
Screenshot of the selector on the Sales Order Screen:
Any help on this will be appreciated

You should have the constructor look something like
public SalesRepCustomer() : base(typeof(Customer.bAccountID))
{
this.DescriptionField = typeof(Customer.acctName);
this.SubstituteKey = typeof(Customer.acctCD);
}
The first type that you pass to the base constructor is the type of the value that will be used for the field.
In this case you want the customer id(an int) and you are currently using the AcctCD(a string) field. The description field would typically be the name of the customer account and the substitute key will make it so that the users see the AcctCD instead of the the customer ID(the actual value) which is just an integer.

Related

Converting data from one class to another

I'm trying to convert data from one ExtensionMethods class to another class called ComboBoxViewItem
ExtensionMethods:
public static class ExtensionMethods
{
public static int GetDisplayItemId(this ComboBox combobox)
{
if (combobox.SelectedItem == null)
return 0;
else
return ((DisplayItems)combobox.SelectedItem).Id; //Error here
}
}
ComboBoxViewItem
class ComboBoxViewItem<T>
{
private string name;
public T Item { get; set; }
public ComboBoxViewItem(T item, string name)
{
this.Item = item;
this.name = name;
}
public ComboBoxViewItem(T item)
{
var prop = item.GetType().GetProperty("Name");
if (prop == null)
throw new ArgumentException("This object does not have a Name property, please use the other contructor.");
if (prop.PropertyType != typeof(string))
throw new ArgumentException("The property Name MUST be of type string. Please use the other contructor instead.");
this.Item = item;
this.name = (string)prop.GetValue(item);
}
public override string ToString()
{
return name;
}
}
Step 1 Now what I'm trying to do is load data from my WCF service into one of my comboboxes like this:
public async Task LoadCompanies()
{
using (MKCServiceClient service = new MKCServiceClient())
{
var companies = await service.GetCompaniesAsync();
foreach (var company in companies)
cmbQuoteCompany.Items.Add(new ComboBoxViewItem<Company>(company));
cmbQuoteCompany.SelectedIndex = 0;
}
} //Coding works fine and loads data into the combobox
Step 2 I want to add that data that was selected in the combobox to be added else where in another table using this method below:
private async void btnQuoteAdd_Click(object sender, RoutedEventArgs e)
{
using (MKCServiceClient service = new MKCServiceClient())
{
quoteInformation = await service.GetQuoteAsync(new QuoteData
{
CompanyId = cmbQuoteCompany.GetDisplayItemId(), //I use ExtensionMethods class here
BranchId = cmbQuoteBranch.GetDisplayItemId(), //Here
CustomerId = cmbQuoteContact.GetDisplayItemId(), //Here
CustomerRFQ = txtQuoteCustomerRFQ.Text,
Date = dpQuoteDate.Text,
Item = txtQuoteItem.Text,
Material = txtQuoteMaterial.Text,
Description = txtQuoteDescription.Text,
Quantity = Convert.ToInt32(txtQuoteQTY.Text)
});
await service.FinalizeQuoteAsync(finalizeQuote);
}
}
My end goal is to get the Id of the selected item in my combobox and then insert it into my database.
After I call the btnQuoteAdd_Click method, my application crashes and gives me the following error
Unable to cast object of type
'MKCWorkflowApplication.ComboBoxViewItem`1[MKCWorkflowApplication.WorkflowService.Company]'
to type 'MKCWorkflowApplication.DisplayItems'.
The reason why i'm posting this issue here is because i've been given this coding from a friend who knows a lot more that C# than me and we are no longer in contact, so I don't know how to get past this issue :(
So if anyone could figure out what is going on, please help! Thank you.
Your extension method GetDisplayItemID expects a combobox filled with instances of the classDisplayItem not filled with instances of a ComboBoxViewItem and thus the internal cast fails. A possible workaround could be written if your classes Company, Quote and Customer provide an indentical named property ID.
You could rewrite your ComboBoxViewItem<T> class in this way
class ComboBoxViewItem<T>
{
public string Name;
public int ID;
public T Item { get; set; }
public ComboBoxViewItem(T item, string name, int id)
{
this.Item = item;
this.Name = name;
this.ID = id;
}
public ComboBoxViewItem(T item)
{
var prop = item.GetType().GetProperty("Name");
if (prop == null)
throw new ArgumentException("This object does not have ...");
if (prop.PropertyType != typeof(string))
throw new ArgumentException("The property Name MUST be of type...");
this.Name = (string)prop.GetValue(item);
prop = item.GetType().GetProperty("ID");
if (prop == null)
throw new ArgumentException("This object does not have ...");
if (prop.PropertyType != typeof(int))
throw new ArgumentException("The property ID MUST be of ...");
this.ID = (int)prop.GetValue(item);
this.Item = item;
}
public override string ToString()
{
// C# 6.0 string interpolation
//return string.Format($"{ID}, ({Name})");
// C# Standard string formatting
return string.Format("{0}, ({1})", ID, Name);
}
}
Now you could retrieve the ID from your comboboxes using this syntax without using the old extension method or defining a new one.
CustomerId = (cmbQuoteContact.SelectedItem as ComboBoxViewItem<Customer>).Item.ID
....

Entity Framework - Many to Many relationship not saving to database

I have stumbled upon a problem with Entity Framework this morning.
I have following code mapping a modified entity and saving it into database.
public Group Save(Group x)
{
using (var db = new HostContext())
{
db.Projects.Attach(x.Project);
if (x.ID != 0)
{
db.AttachableObjects.Attach(x);
var manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
manager.ChangeObjectState(x, EntityState.Modified);
}
else
{
db.AttachableObjects.Add(x);
}
db.SaveChanges();
return x;
}
}
I call Save method with existing group as a parameter. Group contains one user I want to add as a member.
The method finishes successfully, however the relationship is not persisted in database.
Any help is very appreciated.
EDIT: These are my classes
class User : AttachableObject
{
...
private List<Group> memberof;
[DataMember]
[InverseProperty("Members")]
public List<Group> MemberOf
{
get { return memberof; }
set { memberof = value; }
}
...
}
class Group : AttachableObject
{
...
private List<User> members;
[DataMember]
[InverseProperty("MemberOf")]
public List<User> Members
{
get { return members; }
set { members = value; }
}
...
}
EDIT2: This is where the Save method is called
public Group AcceptInvite(int id)
{
var mapper = new InviteMapper();
var userMapper = new UserMapper();
var groupMapper = new GroupMapper();
var invite = mapper.Find(id);
if (invite != null)
{
var group = groupMapper.Find(invite.GroupID);
var user = userMapper.Find(invite.InviteeID);
group.Members.Add(user);
mapper.Delete(invite.ID);
return groupMapper.Save(group);
}
return null;
}
EDIT3: My mappers
public class GroupMapper
{
public Group Find(int id)
{
using (var db = new HostContext())
{
return db.AttachableObjects
.Include("Project")
.OfType<Group>().FirstOrDefault(x => x.ID == id);
}
}
}
The rest of the mappers is the same, only using their own tables.
You are not changing the relationship info of Project, you are only setting x to modified, relationship info must be changed explicitly.
So x.Project must have some property that points back to Group, you need to set it so the change is recorded.
I am guessing that x is resurrected via some deserialization process?

Silverlight 4 with RIA - Binding joined table field to grid

I have 2 tables that has a 1-1 relationship
I am attempting to use the .Include() for LINQ to try to pass a child table entity to a property so that I can bind a child table field to a grid, along with fields from the parent table. Originally it works fine like this and I'm able to bind to the grid but only get results from the BUGroupBuildings table. I also need to bind to a field in the vwBuisnessUnits table.
public IQueryable<BUGroupBuilding> GetBusinessUnitsBasedOnGroupID(int i)
{
var result = from d in this.ObjectContext.BUGroupBuildings
join b in this.ObjectContext.vwBusinessUnits on d.BU equals b.BU
where d.BUGroupID == i
orderby d.BU ascending
select d;
return result;
}
When I switch over to use the Include to bring back child table fields, I get an error
public IQueryable<BUGroupBuilding> GetBusinessUnitsBasedOnGroupID(int i)
{
var result = from d in this.ObjectContext.BUGroupBuildings
.Include("vwBuisnessUnits")
select d;
result = result.Where(w => w.BUGroupID == i).OrderBy(o => o.vwBusinessUnit.BU);
return result;
}
ERROR:
Load operation failed for query 'GetBusinessUnitsBasedOnGroupID'. A
specified Include path is not valid. The EntityType
'EQUITYDWModel.BUGroupBuilding' does not declare a navigation property
with the name 'vwBuisnessUnits'
Here's my entity
I've added the necessary [Include] in the metadata.
[MetadataTypeAttribute(typeof(BUGroupBuilding.BUGroupBuildingMetadata))]
public partial class BUGroupBuilding
{
internal sealed class BUGroupBuildingMetadata
{
// Metadata classes are not meant to be instantiated.
private BUGroupBuildingMetadata()
{
}
public string BU { get; set; }
[Include]
public BUGroup BUGroup { get; set; }
public int BUGroupBuildingsID { get; set; }
public Nullable<int> BUGroupID { get; set; }
[Include]
public vwBusinessUnit vwBusinessUnit { get; set; }
}
}
Did you generate the EntityModel from db or create it manually? Are you create a metada class manually?
Maybe some are wrong. Should be create a navigation property on client side (Web.g.cs file) like this:
/// <summary>
/// Gets or sets the associated <see cref="tblCustomer"/> entity.
/// </summary>
[Association("tblCustomer_tblInvoice", "uiCustomerId", "Id", IsForeignKey=true)]
[XmlIgnore()]
public tblCustomer tblCustomer
{
get
{
if ((this._tblCustomer == null))
{
this._tblCustomer = new EntityRef<tblCustomer>(this, "tblCustomer", this.FiltertblCustomer);
}
return this._tblCustomer.Entity;
}
set
{
tblCustomer previous = this.tblCustomer;
if ((previous != value))
{
this.ValidateProperty("tblCustomer", value);
if ((previous != null))
{
this._tblCustomer.Entity = null;
previous.tblInvoices.Remove(this);
}
if ((value != null))
{
this.uiCustomerId = value.Id;
}
else
{
this.uiCustomerId = default(Guid);
}
this._tblCustomer.Entity = value;
if ((value != null))
{
value.tblInvoices.Add(this);
}
this.RaisePropertyChanged("tblCustomer");
}
}
}
Please check the Entity Model and a relationships.
Ahhh. I found the solution to my problem. Thought I would share it with the community.
It turns out that I have to do a join and an Inlcude in the linq query when I have a custom relationship between 2 entities (in my case, a view and a table). When a relationship is defined in the model, it doesn't require an explicit join.
public IQueryable<BUGroupBuilding> GetBusinessUnitsBasedOnGroupID(int i)
{
var result = from d in this.ObjectContext.BUGroupBuildings
join b in this.ObjectContext.vwBusinessUnits on d.BU equals b.BU
where d.BUGroupID == i
orderby d.BU ascending
select d;
var r2 = from d2 in ((ObjectQuery<BUGroupBuilding>)result)
.Include("vwBusinessUnit")
select d2;
return r2;
}

the sequence does not have elements executing linq query

Having this model class
public class Usuario
{
#region Atributos
private int _intID = 0;
private Perfil _Perfil_FK = null;
private String _strNombre = "";
private String _strPassword = "";
#endregion
#region Propiedades
public int ID
{
get { return _intID; }
set { _intID = value; }
}
public virtual Perfil Perfil_FK
{
get { return _Perfil_FK; }
set { _Perfil_FK = value; }
}
public int PerfilID { get; set; }
public String Nombre
{
get { return _strNombre; }
set { _strNombre = value; }
}
public String Password
{
get { return _strPassword; }
set { _strPassword = value; }
}
#endregion
}
I'm trying to use this query, the data base table have data, so i can't see the problem?
Usuario user = (from U in _db.Usuario
where ((U.Nombre == model.UserName) && (U.Password == encripPassword))
select U).First();
If you need more info about the data base let me know to update my question
Does the table actually have a row with that name and password?
When you run the generated SQL statement in SQL Management Studio, do you get a result?
Suggest checking your two values in UserName and encripPassword for valid values.
Usuario user = _db.Usuario
.FirstOrDefault(x=>x.Nombre == model.UserName
&& x.Password == encripPassword);
string sql = (user as ObjectQuery).ToTraceString(); //the SQL query generated.
if(user==null)
{
//doesn't exist.
}
Obviously you're not pointing to the right database, or your don't have a user name with that password (I'm guessing the encrypted password doesn't match what's in the DB. Try using FirstOrDefault(), then you can check for Null if the password is wrong...
Check if your class has the proper attributes that tell Linq to SQL how to match it against a database table.
Your class should have
[Table(Name="Nombres")]
attribute, and properties should have
[Column]
attributes, some should also be primary keys etc.
If you don't already have this, I suggest you generate the class using the Entity Class Generation tool: http://msdn.microsoft.com/en-us/library/bb425822.aspx#linqtosql_topic32

Need a refresher course on property access

I need help with accessing class properties within a given class.
For example, take the below class:
public partial class Account
{
private Profile _profile;
private Email _email;
private HostInfo _hostInfo;
public Profile Profile
{
get { return _profile; }
set { _profile = value; }
}
public Email Email
{
get { return _email; }
set { _email = value; }
}
public HostInfo HostInfo
{
get { return _hostInfo; }
set { _hostInfo = value; }
}
In the class "Account" exists a bunch of class properties such as Email or Profile.
Now, when I want to access those properties at run-time, I do something like this
(for Email):
_accountRepository = ObjectFactory.GetInstance<IAccountRepository>();
string username = Cryptography.Decrypt(_webContext.UserNameToVerify, "verify");
Account account = _accountRepository.GetAccountByUserName(username);
if(account != null)
{
account.Email.IsConfirmed = true;
But, I get "Object reference not set..." for account.Email... Why is that?
How do I access Account such that account.Email, account.Profile, and so on
returns the correct data for a given AccountId or UserName.
Here is a method that returns Account:
public Account GetAccountByUserName(string userName)
{
Account account = null;
using (MyDataContext dc = _conn.GetContext())
{
try
{
account = (from a in dc.Accounts
where a.UserName == userName
select a).FirstOrDefault();
}
catch
{
//oops
}
}
return account;
}
The above works but when I try:
account = (from a in dc.Accounts
join em in dc.Emails on a.AccountId equals em.AccountId
join p in dc.Profiles on em.AccountId equals p.AccountId
where a.UserName == userName
select a).FirstOrDefault();
I am still getting object reference exceptions for my Email and Profile
properties. Is this simply a SQL problem or is there something else I need to be
doing to be able to fully access all the properties within my Account class?
Thanks!
Your getting this because Email is another class which has not been assigned yet. What you can do is in your constructor default the properties that link to other classes as new items. For example in your ctor:
public Account()
{
// Set Defaults
Email = new Email();
Profile = new Profile();
HostInfo = new HostInfo();
}
Then you can set their values as desired.
This looks like a case of handling null values on your properties. You should initialize the Email property to something other than null if you expect to store or query against it, or alter the queries so that they can expect to deal with null values. Also if you get a null value from the database, and your property cannot be set to null, the reverse problem occurs.
Are you declaring these properties yourself, or are you trying to indicate something like auto-generated code from like Linq-to-SQL?
If this is auto-generated where the Account table references the Email table, etc., then you probably just need to specify that you want those objects to load as well in the load options:
using (MyDataContext dc = _conn.GetContext())
{
var options = new DataLoadOptions();
options.LoadWith<Account>(a => a.Email);
options.LoadWith<Account>(a => a.Profile);
options.LoadWith<Account>(a => a.HostInfo);
dc.LoadOptions = options;
try
{
account = (from a in dc.Accounts
where a.UserName == userName
select a).FirstOrDefault();
}
catch
{
//oops
}
}
Just wanted to add: there is now a shorter form for declaring trivial properties:
public Profile Profile { get; set; }
public Email Email { get; set; }
public HostInfo HostInfo { get; set; }

Categories