Partition Key Question - linq

Let's I define my entity like this:
public class User : TableServiceEntity
{
/// <summary>Unique Id (Row Key)</summary>
public string Id
{
get { return RowKey; }
set { RowKey = value; }
}
/// <summary>Gets or sets the username (Partition Key)</summary>
public string Username
{
get { return PartitionKey; }
set { PartitionKey = value; }
}
}
will the following Linq query use the partition key or not?
var ctx = ServiceLocator.Get<UserDataServiceContext>();
return ctx.Users.Where(x => x.Username == Username).FirstOrDefault();

Pretty sure it will not. You can verify this with Fiddler however. If you do not see a $filter on the wire with PartitionKey and RowKey in it, then you are not querying by them (which is probably bad). I believe what you will have on the wire is an entity that has 4 properties (PK, RK, Id, and Username) and the properties that you will be querying on will not be indexed.

Related

Get GraphQL query name from the entire query text in graphql-java

Assume that there is a query such as:
query test1 {
students {
id
name
address_pin
address_city
yearAdmitted
laptopOS
phoneOS
}
}
I can access this entire query text at runtime in graphql-java.
Is there anyway to get the query-name 'students'?
Tried this and it works for basic queries, not sure if this is the correct way to do so.
private String getQueryName(String input) {
Document doc = new Parser().parseDocument(input);
List<OperationDefinition> definitionList = doc.getDefinitionsOfType(OperationDefinition.class);
for (OperationDefinition definition : definitionList) {
if (definition != null && definition.getOperation().toString().equalsIgnoreCase("QUERY")) {
Field field = definition.getSelectionSet().getSelectionsOfType(Field.class).stream().findFirst().orElse(null);
if (field != null) {
return field.getName();
}
}
}
return null;
}

Performance Issue with NHibernate Query

I am currently having a performance problem with the following query written in NHibernate. I am trying to transform the data I queried into DTO's. With this complex structure I cannot use QueryOver to transform the entities. On the other side Linq provider is so useful but it takes ~10 seconds to load and transform ~6000 entities with each 30 child items. It creates an SQL query with left outer join. Are there any other ways to write this query with a better approach?
var Entities = session.Query<crmEntity>()
.Where(x => x.EntityType.ID == EntityType)
.Select(entity => new EntityDTO()
{
ID = entity.ID,
EntityType = entity.EntityType.ID,
InstanceID = entity.Instance.ID,
Values = entity.Values.Select(
value => new CustomFieldValueDTO()
{
ID = value.ID,
FieldID = value.Field.ID,
Value = value.Value
}).ToList<CustomFieldValueDTO>()
}).ToList();
Here is my solution. if there is any other better way, I am completely open to it:
session.CreateQuery(#"select vals.ID,
vals.Field.ID,
vals.Value,
ent.ID
from crmEntity ent inner join ent.Values vals
with vals.Value IS NOT NULL
where ent.EntityType.ID=:eID and ent.Instance.ID=:instanceID order by ent.ID")
.SetGuid("instanceID", InstanceID)
.SetGuid("eID", EntityType)
.SetResultTransformer(new EntityListTransformer()).Future<ReadOnlyEntityDTO>();
And this is my custom result transformer to get the same hierarchy like my linq query
public class EntityListTransformer : IResultTransformer
{
private List<ReadOnlyEntityDTO> list;
private ReadOnlyEntityDTO lastEntity;
private Guid instanceID;
public EntityListTransformer()
{
list = new List<ReadOnlyEntityDTO>();
lastEntity = new ReadOnlyEntityDTO();
}
public System.Collections.IList TransformList(System.Collections.IList collection)
{
return list;
}
public object TransformTuple(object[] tuple, string[] aliases)
{
string ValueID = tuple[0].ToString();
string FieldID = tuple[1].ToString();
string Value = (string)tuple[2];
string EntityID = tuple[3].ToString();
if (lastEntity.ID != EntityID)
{
if (lastEntity.ID != null)
{
list.Add(lastEntity);
}
lastEntity = new ReadOnlyEntityDTO()
{
ID = EntityID
};
}
lastEntity.Values.Add(new ReadOnlyCustomFieldValueDTO()
{
FieldID = FieldID,
ID = ValueID,
Value = Value
});
return tuple;
}
}

Trying to save comma-separated list

Trying to save selections from a CheckBoxList as a comma-separated list (string) in DB (one or more choices selected). I am using a proxy in order to save as a string because otherwise I'd have to create separate tables in the DB for a relation - the work is not worth it for this simple scenario and I was hoping that I could just convert it to a string and avoid that.
The CheckBoxList uses an enum for it's choices:
public enum Selection
{
Selection1,
Selection2,
Selection3
}
Not to be convoluted, but I use [Display(Name="Choice 1")] and an extension class to display something friendly on the UI. Not sure if I can save that string instead of just the enum, although I think if I save as enum it's not a big deal for me to "display" the friendly string on UI on some confirmation page.
This is the "Record" class that saves a string in the DB:
public virtual string MyCheckBox { get; set; }
This is the "Proxy", which is some sample I found but not directly dealing with enum, and which uses IEnumerable<string> (or should it be IEnumerable<Selection>?):
public IEnumerable<string> MyCheckBox
{
get
{
if (String.IsNullOrWhiteSpace(Record.MyCheckBox)) return new string[] { };
return Record
.MyCheckBox
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(r => r.Trim())
.Where(r => !String.IsNullOrEmpty(r));
}
set
{
Record.MyCheckBox = value == null ? null : String.Join(",", value);
}
}
To save in the DB, I am trying to do this in a create class:
proxy.MyCheckBox = record.MyCheckBox; //getting error here
but am getting the error:
Cannot implicitly convert 'string' to System.Collections.Generic.IEnumerable'
I don't know, if it's possible or better, to use Parse or ToString from the API for enum values.
I know that doing something like this will store whatever I put in the ("") into the DB, so it's just a matter of figuring out how to overcome the error (or, if there is an alternative):
proxy.MyCheckBox = new[] {"foo", "bar"};
I am not good with this stuff and have just been digging and digging to come up with a solution. Any help is much appreciated.
You can accomplish this using a custom user type. The example below uses an ISet<string> on the class and stores the values as a delimited string.
[Serializable]
public class CommaDelimitedSet : IUserType
{
const string delimiter = ",";
#region IUserType Members
public new bool Equals(object x, object y)
{
if (ReferenceEquals(x, y))
{
return true;
}
var xSet = x as ISet<string>;
var ySet = y as ISet<string>;
if (xSet == null || ySet == null)
{
return false;
}
// compare set contents
return xSet.Except(ySet).Count() == 0 && ySet.Except(xSet).Count() == 0;
}
public int GetHashCode(object x)
{
return x.GetHashCode();
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var outValue = NHibernateUtil.String.NullSafeGet(rs, names[0]) as string;
if (string.IsNullOrEmpty(outValue))
{
return new HashSet<string>();
}
else
{
var splitArray = outValue.Split(new[] {Delimiter}, StringSplitOptions.RemoveEmptyEntries);
return new HashSet<string>(splitArray);
}
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var inValue = value as ISet<string>;
object setValue = inValue == null ? null : string.Join(Delimiter, inValue);
NHibernateUtil.String.NullSafeSet(cmd, setValue, index);
}
public object DeepCopy(object value)
{
// return new ISet so that Equals can work
// see http://www.mail-archive.com/nhusers#googlegroups.com/msg11054.html
var set = value as ISet<string>;
if (set == null)
{
return null;
}
return new HashSet<string>(set);
}
public object Replace(object original, object target, object owner)
{
return original;
}
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
public SqlType[] SqlTypes
{
get { return new[] {new SqlType(DbType.String)}; }
}
public Type ReturnedType
{
get { return typeof(ISet<string>); }
}
public bool IsMutable
{
get { return false; }
}
#endregion
}
Usage in mapping file:
Map(x => x.CheckboxValues.CustomType<CommaDelimitedSet>();

No generic method 'Where' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments

I want to retrieve a specific record using IQueryable. But i get error 'No generic method 'Where' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.'. I got the selected row id, but I cannot display it out. Here is my code.
internal static IQueryable GetRecordsFromPrimaryKeys(this IQueryable datasource, List<FilterDescriptor> primaryKeys)
{
IQueryable data = datasource;
ParameterExpression paramExp = null;
bool firstLoop = false;
System.Linq.Expressions.Expression predicate = null;
var RecordType = datasource.GetObjectType();
paramExp = RecordType.Parameter();
foreach (FilterDescriptor primaryKey in primaryKeys)
{
if (!(firstLoop))
{
predicate = data.Predicate(paramExp, primaryKey.ColumnName, primaryKey.Value, FilterType.Equals, false, RecordType);
firstLoop = true;
}
else
{
predicate = predicate.AndPredicate(data.Predicate(paramExp, primaryKey.ColumnName, primaryKey.Value, FilterType.Equals, false, RecordType));
}
}
if (paramExp != null && predicate != null)
{
var lambda = Expression.Lambda(predicate, paramExp);
data = data.Provider.CreateQuery(
Expression.Call(
typeof(Queryable),
"Where",
new Type[] { data.ElementType },
data.Expression,
lambda
)
);
}
return data;
}
My Code works well for IEnumerable/IQueryable/ICollection . But it throws the exception when i specify the class with the keyword virtual and type as ICollection. My code is
public class RoomType
{
public int ID { get; set; }
[MaxLength(10, ErrorMessage = "Room code cannot be longer than 10 characters.")]
public string Code { get; set; }
[MaxLength(50, ErrorMessage = "Room name cannot be longer than 50 characters.")]
public string Name { get; set; }
public virtual ICollection<RoomCategory> RoomCategories { get; set; }
}
Some random values gets appended to 'RecordType' while using the keyword 'virtual'. I think this leads to the exception. Still searching for the solution.
I don't know what is going wrong . Any suggestions welcome.
Thanks.
I just ran into a similar situation. The problem stems from the fact that in some cases you're dealing with the "proxy" not the actual entity. So, you want to make sure that RecordType matches data.ElementType.
try:
var recordType = datasource.GetObjectType();
// make sure we have the correct type (not the proxy)
if (recordType.BaseType.Name != "Object")
recordType = recordType.BaseType;
Or better yet, try:
var recordType = data.ElementType
Try to use typeof(Enumerable) instead of typeof(Queryable)

How do I load an entity from a database where the Composite ID of the record contains a key-property with a null value?

I am reading data from an external organisation using Oracle.DataAccess through the Oracle10g provider. One of the tables has a composite id made up of these fields.
course:
institutioncode: "X11"
coursecode: "N100"
campuscode: "A"
entryyear: 2011
entrymonth: 10
The problem is that the campus code is allowed to be null by the external provider instead of empty. This leads to nHibernate returning collections that contain null references instead of course entities.
Other domain objects will use these fields to refer to this course entity as well, so this is actually used as a key and I can't easily remap to use a surrogate key.
From the source in tag 3.1.0GA, the check that is causing this behavior can be found in Nhibernate.Type.ComponentType.Hydrate(IDataReader rs, string[] names, ISessionImplementor session, object owner). This is always refusing the possibility that a key-property could be null. Could this change to make nullability an option on key-property and key-reference properties?
Failing that, how would you recommend reading this data directly with nHibernate?
NULL values in properties are not supported by design.
There are two ways to deal with this:
Import the data instead of using it raw from the source, adding a proper surrogate key.
Handle that entity without NHibernate.
ok my first comment didnt worked out on References (ManyToOne). So here my alternative solution: a usertype to work around the check.
class CourseMap : ClassMap<Course>
{
public CourseMap()
{
CompositeId()
.KeyProperty(c => c.InstitutionCode)
.KeyProperty(c => c.CourseCode)
.KeyProperty(c => c.CampusCode, key => key.Type(typeof(MyUserType)))
.KeyProperty(c => c.EntryYear)
.KeyProperty(c => c.EntryMonth);
}
}
class MyUserType : IUserType
{
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
public new bool Equals(object x, object y)
{
return object.Equals(x, y);
}
public int GetHashCode(object x)
{
return (x == null) ? 0 : x.GetHashCode();
}
public bool IsMutable
{
get { return false; }
}
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var value = NHibernateUtil.String.NullSafeGet(rs, names[0]);
return (value == null) ? string.Empty : value;
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
string d = string.IsNullOrEmpty((string)value) ? null : (string)value;
NHibernateUtil.String.NullSafeSet(cmd, d, index);
}
public object Replace(object original, object target, object owner)
{
return DeepCopy(original);
}
public Type ReturnedType
{
get { return typeof(string); }
}
public SqlType[] SqlTypes
{
get { return new[] { SqlTypeFactory.GetString(100) }; }
}
}
class SomeEntityMap : ClassMap<SomeEntity>
{
public EntityMap()
{
Id(e => e.Id).GeneratedBy.Assigned();
References(e => e.Course)
.Columns("InstitutionCode", "CourseCode", "CampusCode", "EntryYear", "EntryMonth")
.Fetch.Join(); // important because we can't rely on values, NULL is invalid value
}
}

Resources