Breeze assumes EntityType is in same namespace as EntityContainer. Shouldn’t Breeze consider if the EntityType value is a qualified type name? - metadata

Library: BreezeJS 1.4.11 client-side library
Data Service Adapter: webApiOdata
Remote Service: Web API 2 Implementation of OData v3 without Breeze Controller
I’m working with metadata from a service provider that prefers to keep the EntityContainer in a separate namespace than the EntityTypes it contains. Here are a couple examples similar to our setup:
Default configuration for Web API OData:
http://aspnetwebstack.codeplex.com/workitem/1579
Northwind OData Sample Service:
http://services.odata.org/Northwind/Northwind.svc/$metadata
Consider Breeze’s CsdlMetadataParser.parse function:
var entityTypeName = parseTypeName(entitySet.entityType, schema).typeName;
This function assumes the EntityType has the same schema as the EntityContainer and does not consider the namespace provided in the EntityType attribute of the EntitySet.
A simple Breeze query for the top 10 "Categories" from the Northwind service causes the following error:
Error: Unable to locate a 'Type' by the name: 'Category:#ODataWebV3.Northwind.Model'. Be sure to execute a query or call fetchMetadata first.
Shouldn’t Breeze consider if the EntityType value is a qualified type name and use that rather than assuming the container’s namespace?
Update:
Breeze also assumes that the associations[] for a given navigation property are always in the same schema as the nav property. This causes the following error when attempting to import the metadata for associations in separate namespaces:
"Cannot read property 'end' of null"
The error stems from Breeze's parseCsdlNavProperty and getAssociation functions:
function parseCsdlNavProperty(entityType, csdlProperty, schema) {
var association = getAssociation(csdlProperty, schema);

Replacing this line in Breeze's CsdlMetadataParser.parse method:
var entityTypeName = parseTypeName(entitySet.entityType, schema).typeName;
With this:
var entityTypeName = parseTypeName(entitySet.entityType).typeName;
Resolves the issue and properly considers the namespace inside of the EntityType string. I'd like feedback from Ward or someone else on if this is the right strategy. Also, curious the reasoning behind passing the schema into parseTypeName method, in case removing it will break something else.
Update:
In order to support associations across schemas, I was able to resolve this issue by passing the schemas array all the way from CsdlMetadataParser.parse to getAssociation(). Once getAssociation has access to the schemas array, the following two updates resolve import by removing assumptions and looking at the namespace provided:
function getAssociation(csdlNavProperty, schemas) {
var assocName = parseTypeName(csdlNavProperty.relationship).shortTypeName;
var navSchema = __arrayFirst(schemas, function (sch) {
return sch.namespace === parseTypeName(csdlNavProperty.relationship).namespace;
});
var assocs = navSchema.association;
...
function parseCsdlNavProperty(entityType, csdlProperty, schema, schemas) {
var association = getAssociation(csdlProperty, schemas);
var toEnd = __arrayFirst(association.end, function (assocEnd) {
return assocEnd.role === csdlProperty.toRole;
});
var isScalar = toEnd.multiplicity !== "*";
var dataType = parseTypeName(toEnd.type).typeName;
...

Modifying "getQualifiedTypeName" in breeze.debug.js did the trick for me...
function getQualifiedTypeName(metadataStore, structTypeName, throwIfNotFound)
{
structTypeName = (structTypeName || "") + "";
var shortTypeName = (structTypeName.indexOf(':#') > -1) ?
structTypeName.substring(0, structTypeName.indexOf(':#')) :
structTypeName;
var result = metadataStore._shortNameMap[shortTypeName];
if (result) return result;
if (isQualifiedTypeName(structTypeName)) return structTypeName;
result = metadataStore._shortNameMap[structTypeName];
...
return result;
}

The latest version of breeze is 1.5.3, and a lot of changes have been made since 1.4.11.
I would try that before 'fixing' the CsdlMetadataParser.

This is still an issue in Breeze 1.5.4. I opened
https://github.com/Breeze/breeze.js/issues/96
for it (seems like a bug more than a SO question?).
The workaround I found is to modify the
if (schema) {
line in function parseTypeNameWithSchema(entityTypeName, schema) ( breeze.debug.js line 7327) so that the function is:
// schema is only needed for navProperty type name
function parseTypeNameWithSchema(entityTypeName, schema) {
var result = parseTypeName(entityTypeName);
if (schema && !result.namespace) {
var ns = getNamespaceFor(result.shortTypeName, schema);
if (ns) {
result = makeTypeHash(result.shortTypeName, ns);
}
}
return result;
}
I believe this is the correct behavior, in that if the EntityType name is fully scoped, its namespace should be used; and if the EntityType is name only, the surrounding schema namespace should be used.

Related

Entity Framework Z Plus Batch Update

I am using Entity Framework Z Plus Batch Update method. I am unable to proceed due to below issue. Actually Update method works good when i give static values like tagName="amir1". But I need to get the Tagdescription from a web service or from another collection based on the tagId, Update method is not accepting a extension method or any other method to accomplish my requirement.Its saying
"LINQ to Entities does not recognize the method 'System.String GetTagDescription(Int32)' method, and this method cannot be translated into a store expression.".
Hope my requirement is clear now. Please guide me if there is any other approach for my requirement.
Here is my code:
using (var context = new TrialsDBEntities())
{
context.tblTags.Where(x => (tagIdCollection.Contains(x.tagId))).
Update(m => new tblTag { tagDescription = m.tagId.GetTagDescription(), tagName = "amir1" });
}
public static string GetTagDescription(this int i)
{
return "test" + i;
///Replace above line with call to database or web service call
getting some text by giving i as input
}
Disclaimer: I'm the owner of the project Entity Framework Plus
Unfortunately, that's not possible to use BatchUpdate with a value that changes from a row to another.
Disclaimer: I'm the owner of the project Entity Framework Extensions
In this situation, we normally recommend using our paid library that's build for this kind of situation and offer high-performance saving operation.
Example
// Easiest way
context.BulkSaveChanges();
// Fastest way
context.BulkUpdate(tags);
EDIT: Answer comment
If I can't use updated row so why the signature of action is misleading and give me possibility to write like this: e => new Entity { Name = e.Name + "Edited" }
For most providers such as SQL Server, your expression is supported. You give a global expression so we can apply. Your expression doesn't doesn't change from a row to another, it's the same expression.
What is not supported is giving a specific expression from a row to another.
Write your code as follows:
using (var context = new TrialsDBEntities())
{
var tagsToBeUpdated = context.tblTags.Where(x => (tagIdCollection.Contains(x.tagId))).AsNoTracking().ToList();
//Only use this code block if your tagsToBeUpdated list is too large
Parallel.ForEach(tagsToBeUpdated, tagToBeUpdated =>
{
var tagDescription = GetTagDescription(tagToBeUpdated.tagId);
tagToBeUpdated.tagDescription = tagDescription;
context.Entry(tagToBeUpdated).State = EntityState.Modified;
});
//Only use this code block if your tagsToBeUpdated list is not too large
foreach(var tagToBeUpdated in tagsToBeUpdated)
{
var tagDescription = GetTagDescription(tagToBeUpdated.tagId);
tagToBeUpdated.tagDescription = tagDescription;
context.Entry(tagToBeUpdated).State = EntityState.Modified;
}
context.SaveChanges();
}
public static string GetTagDescription(int i)
{
return "test" + i;
///Replace above line with call to database or web service call
//getting some text by giving i as input
}

Avoid lazyloader attribute

I´ve been looking for how avoid return a list without the attribute lazyLoader, I want to continue using the lazyLoader but I don´t want return the attribute when I return the whole list of my entity from my controller
I´m working with .NET core.
[
{
"lazyLoader": {},
"id": "id1"
"name": "name"
},
{
"lazyLoader": {},
"id": "id2",
"name": "name2"
}
]
You can do a select of you collection only retrieving the rest of the data.
That way your objects will not have the Navigation property at all.
db.YourCollection.Where(your condition)Select(x => new { id = x.id , name = x.name } );
In Entity Framework, if you have an object where one or more of its properties use lazy loading, check its runtime type name using GetType().Name. For an object of a Car class, for example, you will notice that the runtime type is actually something called CarProxy, which is a temporary in-memory type created by Entity Framework using reflection. This "fake" proxy class's base type is Car, and has all the original Car properties, but includes an extra one called LazyLoader for properties that may need it.
If you do further checking on this "fake" CarProxy type, you will also see that Assembly.IsDynamic = true, which is indicative that the class was created dynamically using reflection (see documentation):
var TheCar = DBContext.Cars.Find(1);
Console.WriteLine(TheCar.GetType().Assembly.IsDynamic.ToString()); //will echo "true"
Luckily, Newtonsoft.Json has an override on the JsonConvert.SerializeObject() method that allows us to provide a base type, so that the resulting JSON doesn't contain properties that don't exist in that type. So, to eliminate the LazyLoader property, just provide the object's BaseType as the type parameter:
var TheCar = DBContext.Cars.Find(1);
var TheJSON = Newtonsoft.Json.JsonConvert.SerializeObject(TheCar, TheCar.GetType().BaseType);
To make sure you don't get any circular reference loops when serializing (a very high probability when using lazy loading), call the serializer with the following setting:
var TheCar = DBContext.Cars.Find(1);
var Settings = new Newtonsoft.Json.JsonSerializerSettings
{
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};
var TheJSON = Newtonsoft.Json.JsonConvert.SerializeObject(TheCar, TheCar.GetType().BaseType, Settings);
Note: This may only work on the first level deep when the serializer travels through the object. If there are yet more lazy-loading child properties of the object you provide to the serializer, the "LazyLoader" property may appear again. I haven't tested it so I can't say for sure.
I know this is old, but add
public boolean ShouldSerializeLazyLoader() { return false; }
to all the classes down the tree of the ones you want to serialize, and you will get a lazyloader free JSON.
Ref.: https://www.newtonsoft.com/json/help/html/ConditionalProperties.htm
The checked answer for this question is just working for the root object, if we have many nested lazyloaded objects, this solution will not work.
Although the answer of #Marcello-Barbiani is correct but it is not a good way to add this function to all entities we have.
The best way is create a new ContractResolver derived from DefaultContractResolver and check if property is Lazyloader then skip it as below:
public class NonLazyloaderContractResolver : DefaultContractResolver
{
public new static readonly NonLazyloaderContractResolver Instance = new NonLazyloaderContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.PropertyName == "LazyLoader")
{
property.ShouldSerialize = i => false;
}
return property;
}
}
after that adding above class pass it through JsonSerializerSettings while serializing the object:
var json = JsonConvert.SerializeObject(newProduct, new JsonSerializerSettings() {
ContractResolver = new NonLazyloaderContractResolver(),
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore });
and finally if you are using asp.net core or asp.net core webapi add this contract as default contractresolver in startup.cs file:
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new NonLazyloaderContractResolver();
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});

Trying to add Support For Index Usage on EFCore Spanner Provider

I'm writing a driver for EF Core for Spanner - In basic level it works and I can write Read and Write Queries that get's translated to Spanner SQL , executed and return results etc..
Now I'm trying to add Support For Read Query with Secondary Index.
Ultimately I'm trying to generate this SQL Query:
SELECT * FROM PostTags#{ FORCE_INDEX = PostTagsByTagId } WHERE TagId = 1
From This Linq:
var postTag = ctx.PostTags.WithIndex("PostTagsByTagId").Where(x => x.TagId == 1).FirstOrDefault();
I've added extension method as follow:
public static class SpannerIndexSupport
{
public static IQueryable<TSource> WithIndex<TSource>(this IQueryable<TSource> query, string indexName)
{
var methodDefinition = typeof(SpannerIndexSupport).GetTypeInfo().GetMethods().Single(m => m.Name == "WithIndex");
var method = methodDefinition.MakeGenericMethod(typeof(TSource));
var args = new[] { query.Expression, Expression.Constant(indexName) };
var expression = Expression.Call(null, method, args);
return query.Provider.CreateQuery<TSource>(expression);
}
}
And tried to write IAsyncQueryProvider to support it but couldn't find a way to make it work.
Any ideas Anyone?
In the official Spanner EFCore library (https://github.com/GoogleCloudPlatform/google-cloud-dotnet/tree/master/apis/Google.Cloud.EntityFrameworkCore.Spanner/Google.Cloud.EntityFrameworkCore.Spanner), I would start by overriding VisitTable(TableExpression tableExpression) in SpannerQuerySqlGenerator:
https://github.com/GoogleCloudPlatform/google-cloud-dotnet/tree/master/apis/Google.Cloud.EntityFrameworkCore.Spanner/Google.Cloud.EntityFrameworkCore.Spanner/Query/Sql/Internal/SpannerQuerySqlGenerator.cs
This will allow you to get a proof of concept going because you can directly influence the generated SQL text there.
Once that works, then you will want to make it proper.
I suppose there might be a few ways to make this work. The simplest might be to have some custom no-op method marker in the Linq expression tree and then register an IMethodCallTranslator to convert it either to a custom spanner specific Expression (whose Accept calls into SqlGenerator to generate the proper Sql) or possibly creating a SqlTranslatingExpressionVisitor to switch out the table expression to a custom one that allows the FORCE_INDEX.
Sorry I couldn't help more.
This is now supported in the official Entity Framework provider for Google Cloud Spanner. You can add this by adding a tag to the query like this:
var singersOrderedByFullName = context.Singers
// This will add the following comment to the generated query:
// `-- Use hint: force_index FullName`
// This comment will be picked up by the interceptor and an index
// hint will be added to the query that is executed.
.TagWith("Use hint: force_index FullName")
.OrderBy(s => s.FullName)
.AsAsyncEnumerable();
A full example can be found here: https://github.com/googleapis/dotnet-spanner-entity-framework/blob/main/Google.Cloud.EntityFrameworkCore.Spanner.Samples/Snippets/QueryHintSample.cs

How to get the maximum length of a string from an EDMX model in code?

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.)

How do you get entity's schema name using Code First (Entity Framework) and DbContext?

Our database was designed in such a way where there are various schemas for production and various equivalent schemas for test. For example, many tables rest in MyProduction schema while the same tables live in MyTest schema.
What I want to do is determine which schema a table is using so I know which one to change it to. So, by default, everything will be under the production schemas. In the OnModelCreating event of the DbContext, if I need to point to test (determined by some true/false configuration), I need to determine the production schema being used, then point it to it's test equivalent.
I'm already aware of how to set the schema but can't find how to get it. Any ideas how I can determine the schema that a table is using?
Thank You.
Try below code after modifying according to you local settings:
var context = new YouDbContext("ConnectionName");
var adapter = (IObjectContextAdapter)context;
var objectContext = adapter.ObjectContext;
EntitySetBase schema = null;
if (objectContext.MetadataWorkspace != null)
{
schema = objectContext.MetadataWorkspace
.GetItems<EntityContainer>(DataSpace.SSpace).First()
.BaseEntitySets
.First(meta => meta.ElementType.Name == "ClassNameUnderYourDbContext");
}
//See the properties of schema in debug mode to understand details
Entity Framework schemas are System.ComponentModel.DataAnnotations.TableAttribute objects. Here are some methods you can use to get an entitity's schema name and table name. Cheers!
private string GetTableName(Type type)
{
var tableAttribute = type.GetCustomAttributes(false).OfType<System.ComponentModel.DataAnnotations.TableAttribute>().FirstOrDefault();
if (tableAttribute != null && !string.IsNullOrEmpty(tableAttribute.Name))
{
return tableAttribute.Name;
}
else
{
return string.Empty;
}
}
private string GetTableSchema(Type type)
{
var tableAttribute = type.GetCustomAttributes(false).OfType<System.ComponentModel.DataAnnotations.TableAttribute>().FirstOrDefault();
if (tableAttribute != null && !string.IsNullOrEmpty(tableAttribute.Schema))
{
return tableAttribute.Schema;
}
else
{
return string.Empty;
}
}
System.ComponentModel.DataAnnotations.Schema.TableAttribute