This is how I am trying to set the LINKMAP property and it is not working. Anyone know how to fix this?
public static void setOrientLinkMapProperty(OVertex srcVertex, String propName, String linkMapKey, OVertex newPropVertex) {
Map<String, ORID> currLinkMap = srcVertex.getProperty(propName);
if (currLinkMap == null) currLinkMap = new HashMap<>();
srcVertex.setProperty(propName, currLinkMap.put(linkMapKey, newPropVertex.getIdentity()), OType.LINKMAP);
srcVertex.save();
}
The pure SQL way of setting the LINKMAP is as follows:
CREATE CLASS Example EXTENDS V
CREATE PROPERTY Example.name STRING
CREATE PROPERTY Example.map LINKMAP
CREATE VERTEX Example SET name = 'a'
CREATE VERTEX Example SET name = 'b'
UPDATE (SELECT FROM Example WHERE name = 'a') SET map = {'b':(SELECT FROM Example WHERE name = 'b')}
I'm not sure why your code is not working, it seems correct.
Perhaps try explicitly initializing the HashMap:
currLinkMap = new HashMap<String, ORID>(); // Explicitly initialized hashmap
currLinkMap.put(linkMapKey, newPropVertex.getIdentity());
srcVertex.setProperty(propName, currLinkMap, OType.LINKMAP);
srcVertex.save();
Do you have any errors when running that code?
Related
I'm trying to figure out how to use Android's Room library for implementing a prepopulated sqlite database in my app and I came across this Android tutorial. One of the lines (the one in the title) confuses me though, because in another tutorial (also by Android), this line isn't present. Why is this line of code present in the first tutorial but not the second? What is its purpose?
I ask this because my code (which I'm basing off the second tutorial) doesn't include this line and yet this post by a different user attempting to do something similar with a prepopulated database does include it.
Here is some of the code I have (each of the fields has a getter method which just returns this.thatfield'sname):
#Entity (tableName = "words")
public class Words {
#PrimaryKey
#NonNull
#ColumnInfo (name = "word_id")
private int wordId;
#ColumnInfo(name = "a_words")
private String aWords;
#ColumnInfo(name = "b_words")
private String bWords;
#ColumnInfo(name = "c_words")
private String cWords;
This code gives me a "Cannot find setter for field" but just changing the fields from public to private seems to solve that (not sure if this is the best way to solve this error, though).
Why is this line of code present in the first tutorial but not the second?
That line is an additional class constructor that takes 1 non-null String and sets the mWord member/variable to the provided String.
Without then you can only use myWord = new Word(); to instantiate a Word object and the value would be either the default value if provided or null.
With the additional constructor then you could use both
myWord = new Word();
or
myOtherWord = new Word("A Word");
So, in short it's provided an alternative way of constructing/instantiating a new Object of that Class.
Using your code then you could have, for example :-
#Entity(tableName = "words")
class Words {
#ColumnInfo(name = "word_id")
#PrimaryKey
private int wordId;
#ColumnInfo(name = "a_words")
String aWords;
#ColumnInfo(name = "b_words")
String bWords;
#ColumnInfo(name = "c_words")
String cWords;
public void setWordId(int wordId, String aWord, String bWords, String c) {
this.wordId = wordId;
this.aWords = aWord;
this.bWords = bWords;
this.cWords = c;
}
}
Note for demonstration the parameter names use 3 different standards, ideally you would stick to a single standard/convention for naming the parameters.
So now you could use the one constructor that expects 4 parameters e.g.
myWord = new Words(1,"Apple","Banana","Cherry");
which equates to
myWord = new Words();
myWord.wordId = 1;
myWord.aWords = "Apple;
myWord.bWords = "Banana";
myWord.cWords = "Cherry";
As you have specified a constructor, the default constructor is no longer usable.
What is its purpose?
As can be seen, additional constructors, can reduce the amount of coding, there use will also prompt for the values (hence the use of useful parameter names improves i.e. c as above is not very meaningful at all (although in conjunction with the other parameters if would be better than x))
I have the following child object that we use an expression to map our 'entity' to our 'domain' model. We use this when specifically calling our ChildRecordService method GetChild or GetChildren:
public static Expression<Func<global::Database.Models.ChildRecord, ChildRecord>> MapChildRecordToCommon = entity => new ChildRecord
{
DateTime = entity.DateTime,
Type = entity.Type,
};
public static async Task<List<ChildRecord>> ToCommonListAsync(this IQueryable<global::Database.Models.ChildRecord> childRecords)
{
var items = await
childRecords.Select(MapChildRecordToCommon).ToListAsync().EscapeContext();
return items;
}
public async Task<List<ChildRecord>> GetChildRecords()
{
using (var uow = this.UnitOfWorkFactory.CreateReadOnly())
{
var childRecords= await uow.GetRepository<IChildRecordRepository>().GetChildRecords().ToCommonListAsync().EscapeContext();
return childRecords;
}
}
So that all works just fine. However we have another object that is a parent to that child, that in SOME cases, we also wish to get the child during the materialisation and mapping process.
In other words the standard object looks as such:
private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommonBasic = (entity) => new Plot
{
Id = entity.Id,
Direction = entity.Direction,
Utc = entity.Utc,
Velocity = entity.Velocity,
};
However what I also want to map is the Plot.ChildRecord property, using the expression MapChildRecordToCommon I have already created. I made a second expression just to test this:
private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommonAdvanced = (entity) => new Plot
{
ChildRecord = MapChildRecordToCommon.Compile() (entity.ChildRecord)
};
This fails:
System.NotSupportedException
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
Is there a way to reuse my existing expression for ChildRecord, to materialise the object of ChildRecord (ie. one to one/singular not multiple) on the Plot object? I think my trouble is caused by there being just one object and being unable to use the .Select(Map) method. I am not too great at expressions and have hit a wall with this.
For reference, there are actually up to 5 or 6 other child objects on the "Plot" object that I also want to make expressions for.
I resolved this by using the third party library LinqKit.
The library allowed the use of 2 methods, .AsExpandable() (which allows for the expressions to properly compile and be invoked as I understand), and .Invoke() as an extension method to an expression, rather than calling Expression.Invoke(yourexpression). I included a null check just in case.
My code now looks as follows:
public static async Task<List<Plot>> ToCommonListAsync(this IQueryable<global::Database.Models.Plot> plots)
{
var items = await
plots.AsExpandable().Select(MapPlotToCommon).ToListAsync().EscapeContext();
return items;
}
private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommon = (entity) => new Plot
{
Id = entity.Id,
Direction = entity.Direction,
Utc = entity.Utc,
Velocity = entity.Velocity,
ChildRecord = entity.ChildRecord != null ? MapChildRecordToCommon.Invoke(entity.ChildRecord) : default
};
public static Expression<Func<global::Database.Models.ChildRecord, ChildRecord>> MapChildRecordToCommon = entity => new ChildRecord
{
DateTime = entity.DateTime,
Type = entity.Type,
};
I've created an EDMX object from a database I'm programming against.
I need to get input from a user and save it to a row in the database table. The problem is that I need to limit the length of input strings to the width of the corresponding VARCHAR column in the database.
When I browse the model, I can clearly see in the properties window that the model knows the max length of the string, but I don't know how to access this data in code.
If I want to write something like this:
Entities entities = new Entities();
myTable = entities.myTable.First();
if (userInput.length > myTable.columnA.MaxLength)
{
// tell the user that the input is too long.
}
else
{
myTable.columnA = userInput;
}
How do I write it?
Update: I would like to point out that the IObjectContextAdapater mentioned in the answers below is in the System.Data.Entity.Infrastructure namespace.
Here are two methods by which you can read the meta data:
int? GetMaxLength(DbContext context, string tableName, string propertyName)
{
var oc = ((IObjectContextAdapter)context).ObjectContext;
return oc.MetadataWorkspace.GetItems(DataSpace.CSpace).OfType<EntityType>()
.Where(et => et.Name == tableName)
.SelectMany(et => et.Properties.Where(p => p.Name == propertyName))
.Select (p => p.MaxLength)
.FirstOrDefault();
}
int? GetMaxLength<T>(DbContext context, Expression<Func<T, object>> property)
{
var memberExpression = (MemberExpression)property.Body;
string propertyName = memberExpression.Member.Name;
return GetMaxLength(context, typeof(T).Name, propertyName);
}
So you can either enter the table name and property name, or an expression that specifies the property you're interested in.
Another approach could be to create a MetaData class and use the MaxLength attribute.
It's not very pretty; reading edmx properties at runtime is not something Microsoft exposed easily or documented well (or in some cases, at all). context is your DBContext.
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
var entityType = objectContext.MetadataWorkspace.GetItems<EntityType>(DataSpace.CSpace).Where(e => e.Name == "your entity name").First();
var facets = entityType.Properties["your property name"].TypeUsage.Facets;
facets will look something like this, so you'll need to look for the MaxLength Name(may not exist, depending on the underlying field type) and get the Value:
Count = 5
[0]: Nullable=false
[1]: DefaultValue=null
[2]: MaxLength=250
[3]: Unicode=false
[4]: FixedLength=false
If you modify the T4 template you can add your own attribute to the properties that have MaxLength set.
If you can find the right place to add it, it's something as simple as this:
var lengthAttributeText = edmProperty.MaxLength.HasValue
? string.Format("[MaxLength({0})] ", edmProperty.MaxLength.Value)
: "";
And then add this into the text for the property line. (Tricky to be more detailed since I've already modified my .tt file a lot; also the lack of proper IDE support for .tt files makes this a lot harder than it could be.)
I'm getting just killed trying to make a class of a class. I have shopped around the site and seen several examples but maybe because its 1:43 I am having a hard time understanding them.
I was successfully able to use a class to automate a huge data entry project at work. I created a class called catDist which is the category distribution of types of agricultural products a company could manufacture or sell.
catDist contains six properties:
Private selfWorth As String
Private Q1 As Double
Private Q2 as Double
Private Q3 as Double
Private Q4 As Double
Private activated As Boolean
They all have the standard get and let codes.
There are 48 possible categories. I have a module that creates 48 instances of them with 48 different values for selfWorth (e.g "Cottonseed", or "maize" etc), and sets Q1 through Q4 as 0 . The module originally worked with a Userform that I could type in the values and hit enter. If it saw that I had entered a value inside a particular textbox (yes there were 48X4 textboxes) it would set activated to true and changes the relevant Q's to the values I entered.
WHAT I WANT TO DO NOW.
It was a great success. Now what I want to do is create a class called "Distributor". Each distributor class would have 4 collections have catDist objects. I can create the distributor class. I can create the catDist class. But for the love of God I can not figure out a way to set the corresponding distributor catDist property to the catDist value I used in the Set method.
Sub testRegist()
Dim registrant As testRegistrant
Set registrant = New testRegistrant
registrant.registNum = "Z000123"
'MsgBox (registrant.registNum)
Dim cowMilk As testcatDist
Set cowMilk = New testcatDist
cowMilk.selfWorth = "Cow Milk"
cowMilk.distribution = 4.6
registrant.testCat = cowMilk
Debug.Print registrant.testCat.selfWorth
End Sub
catDist Class
Private pselfWorth As String
Private pdistribution As Double
Public Property Get selfWorth() As String
selfWorth = pselfWorth
End Property
Public Property Let selfWorth(name As String)
pselfWorth = name
End Property
Public Property Get distribution() As Double
distribution = pdistribution
End Property
Public Property Let distribution(dist As Double)
pdistribution = dist
End Property
Registrant a.k.a distributor class
Private pRegistNum As String
Private pCatDist As testcatDist
Public Property Get registNum() As String
registNum = pRegistNum
End Property
Public Property Let registNum(registration As String)
pRegistNum = registration
End Property
Public Property Get testCat() As testcatDist
testCat = pCatDist
End Property
Public Property Let testCat(cat As testcatDist)
Set pCatDist = New testcatDist
pCatDist = cat
End Property
The only problem I see is that you are using Let instead of Set. In VBA you use Set when assigning to objects.
When you write registrant.testCat = cowMilk (in your Sub), testCat = pCatDist (in the getter of testRegistrant.testCat) and pCatDist = cat (in the setter of testRegistrant.testCat) you are implicitly using Let (it's as if you had written Let registrant.testCat = cowMilk) instead of (explicitly) using Set.
So, if you write Set registrant.testCat = cowMilk in your test Sub, Set testCat = pCatDist in the getter and Set pCatDist = cat in the setter you should be good to go.
Also, in the same setter, the initialization of pCatDist isn't needed since you are passing cat to it in the next line.
And, as #GSerg (thank you) says, the signature of your setter should be Public Property Set testCat(cat as testcatDist) instead of Public Property Let.
Is there any way how to Group By multiple columns dynamically?
Eg. group x by new { x.Column1, x.Column2 }
but the x.Column1 etc. I want to set dynamically (from UI)
The way to achieve this dynamically on db site is quite complicated as we cannot dynamically create anonymous types. To replace them I would suggest to create a class:
public class CustomTuple<T1, T2>
{
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
}
We cannot use Tuple here as it does not have default constructor. In CustomTuple class place as much parameters T and as much properties as you would need at max. If you will define in that class 5 properties but for the query you will use only 3 you just set only 3 properties to proper values and the remaining 2 properties you keep null - the query will still work. Alternatively you may dynamically at run time generate proper class with CodeDOM. Then comes query logic:
Type[] parameterTypes = new Type[] { typeof(int), typeof(object) };
Type tupleType = typeof(CustomTuple<,>).MakeGenericType(parameterTypes);
ParameterExpression x = Expression.Parameter(typeof(Entity));
NewExpression body = Expression.New(tupleType.GetConstructor(new Type[0]), new Expression[0]);
MemberBinding binding1 = Expression.Bind(
typeof(CustomTuple<,>).MakeGenericType(parameterTypes).GetProperty("Item1"),
Expression.Property(x, "Value"));
MemberInitExpression memberInitExpression =
Expression.MemberInit(
body,
binding1);
Expression<Func<Entity, object>> exp = Expression.Lambda<Func<Entity, object>>(memberInitExpression, x);
using (MyDbContext context = new MyDbContext())
{
var list = context.Entities.GroupBy(exp).ToList();
}
The above code groups Entities by Value property. parameterTypes may be dynamically build during program execution - this is list of types of properties anonymous type for key selection in group by would have. Basing on that we create proper CustomTuple type. Then we dynamically create at run time binding1 elements - one per each property to be set for grouping key. In the example above I create only one. With use of the NewExpression and MemberBinding expression we may build initialization expression with MemberInit method. Finally you build lambda expression from that and execute it against db.