I am interested in creating a OData wcf data service using visual studio 2012. However I do not want to use an entity model framework but rather use my scheme less nosql dataset to store and retrieve the data.
Is there a way that allows me to take control of the odata services without being tide into a specific class structure such as Microsoft's entity framework.
You can use the Microsoft OData implementation without the Entity Framework. What you do need is an implementation of IQueryable. Here's an example OData service querying an array of objects:
using System.Web.Http;
using System.Web.Http.OData;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Query;
// GET api/values
[ActionName("FromList")]
public IList<Poco> GetFromList(ODataQueryOptions<Poco> queryOptions)
{
IQueryable<Poco> data = (
new Poco[] {
new Poco() { id = 1, name = "one", type = "a" },
new Poco() { id = 2, name = "two", type = "b" },
new Poco() { id = 3, name = "three", type = "c" }
})
.AsQueryable();
var t = new ODataValidationSettings() { MaxTop = 25 };
queryOptions.Validate(t);
var s = new ODataQuerySettings() { PageSize = 25 };
IEnumerable<Poco> results =
(IEnumerable<Poco>)queryOptions.ApplyTo(data, s);
return results.ToList();
}
public class Poco
{
public int id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
Related
I know the include-feature of RavenDB. It allows me to fetch a referenced document right away in one roundtrip to the database. But my problem is: The document i fetch in the first place is not including a reference to the "other" documents. But the "other" documents have references to the current document.
Imagine a setup where we have sites across the world. Each site may trigger various alarms. Each alarm has a reference to the site via siteId.
Now i would like to get a list of all the sites including all alarms. But it looks like, this is not possible with RavenDB? Since include only accepts a "path" in the site-Document which holds an id (or an array of ids) to the referenced document.
This could be solved by providing an array of alarmIds within the site'-document and referencing this array in include. But in contrast to a lot of examples featuring stuff like an orderwithlineItemswhere the order is a self contained thing, mysite` will be running for years, collecting alarms anywhere between 0 and a million. Which seems to be a bad idea to me.
Of course i could go the other way round: Query all alarms and include the sites via sitesId. But this would not return a site that has zero alarms.
So is this just a design error on my side? To i misunderstand something? Or is it just not possible to do this in one query and prevent a "n+1 query"?
public class A
{
public string Id { get; set; }
}
public class B
{
public string Id { get; set; }
public string A { get; set; }
}
public class MultiMapIndex : AbstractMultiMapIndexCreationTask<MultiMapIndex.Result>
{
public class Result
{
public string Id { get; set; }
public IEnumerable<string> Bs { get; set; }
}
public MultiMapIndex()
{
AddMap<A>(items => from a in items
select new Result {Id = a.Id, Bs = new string[0]});
AddMap<B>(items => from b in items
select new Result {Id = b.A, Bs = new[] {b.Id}});
Reduce = results => from result in results
group result by result.Id
into g
select new Result {Id = g.Key, Bs = g.SelectMany(r => r.Bs)};
}
}
[Fact]
public async Task TestCase()
{
using var store = GetDocumentStore();
await new MultiMapIndex().ExecuteAsync(store);
using (var session = store.OpenAsyncSession())
{
await session.StoreAsync(new B {A = "a/1"}, "b/0");
await session.StoreAsync(new A(), "a/1");
await session.StoreAsync(new A(), "a/2");
await session.SaveChangesAsync();
}
WaitForIndexing(store);
using (var session = store.OpenAsyncSession())
{
var results = await session.Query<MultiMapIndex.Result, MultiMapIndex>()
.Include(r => r.Bs)
.ToArrayAsync();
var before = session.Advanced.NumberOfRequests;
var bs = session.LoadAsync<B>(results[0].Bs);
Assert.Equal(before, session.Advanced.NumberOfRequests);
}
}
If you do choose to query all Alarms, as you mention,
then you can create a Map-Reduce index on the Alarms collection which will group-by the Sites.
Then you can query this Map-Reduce index and know per Site the count of Alarms it has or doesn't have...
https://demo.ravendb.net/demos/csharp/static-indexes/map-reduce-index
I have a class as below and try to use EF core framework to store the below model to Cosmos DB. But the the JSON is stored as
{
"id": "e6b75f1f-0cc2-488c-9074-62e7e85c727a",
"Type ": "testType",
"TagName ": "TagName",
"DictionaryList ": {}
}
public class TestDictionary
{
public Guid Id { get; set; }
public string Type { get; set; }
public string TagName { get; set; }
public Dictionary<string,object> DictionaryList { get; set; }
}
Problem is that the DictionaryList property doesn't save the data that is passed from api. It always stores as empty object instead of data passed through dictionary
I want my Jason to be stored in cosmos as below
{
"id": "e6b75f1f-0cc2-488c-9074-62e7e85c727a",
"Type ": "testType",
"TagName ": "TagName",
"DictionaryList ": {
“Account number”: “123456”,
“Check date”: “11/20/2020”
}
}
Only collections of primitive types are supported by EF Core 6, So DictionaryList needs to be typed as Dictionary<string,string>
Dictionary<string,object> is recognized as a nested entity type, you could use it, but you'd need to explicitly configure all the keys that it can contain:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TestDictionary>()
.OwnsOne(t => t.DictionaryList, c =>
{
c.Property<string>("Type");
c.Property<string>("TagName");
});
}
I later realized that I could using the cosmos sdk insert Dictionary<stringm, object> without any problem, and that Ef core does expose the underlying cosmos client.
So I solved the issue by access the underlying cosmos client, and use that to insert my dictionary.
var cosmosClient = dbContext.Database.GetCosmosClient();
var container = cosmosClient.GetContainer( "db", "container");
await container.CreateItemAsync(new Dictionary<string,object>);
works like a charm.
remember the Id has to be id or atleast serializer has to be told that Id has to be interpreted id
I need to pass a list of integers to a stored procedure because Entity Framework takes too long to process the request. I'm using a User Defined Table Type to do this. I'm using EntityFrameworkExtras.EF6 and I've created a stored procedure and Table Type class to help with this. Here are those classes:
namespace MyModel{
using EntityFrameworkExtras.EF6;
[UserDefinedTableType("SelectedActivity")]
public class ChartCountryUDT
{
[UserDefinedTableTypeColumn(1)]
public int ID { get; set; }
}
[StoredProcedure("GetCountryChartData")]
public class ChartCountryStoredProcedure
{
[StoredProcedureParameter(System.Data.SqlDbType.Udt, ParameterName = "ActivityIDs")]
public List<ChartCountryUDT> ChartCountryUDT { get; set; }
}}
and here is my method to call the stored procedure passing in the Table Type and returning me a List of objects:
public List<ChartCountry> GetCountriesForChart(List<int> activityIDs)
{
using (MyEntities ctx = new MyEntities())
{
var procedure = new ChartCountryStoredProcedure()
{
ChartCountryUDT = new List<ChartCountryUDT>()
{
new ChartCountryUDT() {ID = 1 }
}
};
return (List<ChartCountry>)ctx.Database.ExecuteStoredProcedure<ChartCountry>(procedure);
}
}
As you can see in my ChartCountryUDT object initializer, I'm hardcoding one object by setting the ID value to 1. This works fine but I would like to take the activityIDs parameter that is passed in and create new objects in my object initializer for each ID in the activityIDs parameter. Is there any way of looping trough my list of activityIDs and creating new objects in my object initializer for each record?
Thanks
You are basically asking how to map (convert) List<int> to List<ChartCountryUDT>, which in LINQ is called projection (select):
var procedure = new ChartCountryStoredProcedure()
{
ChartCountryUDT = activityIDs.Select(id => new ChartCountryUDT { ID = id }).ToList()
};
I am a .Net developer and is currently exploring on ArangoDB. I have played around with the arangod web user interface and arangod and like this NoSql very much until I delve into the detail of coding. I could not find the .Net driver working properly. Even for simple CRUD operation. Here's the problem.
ArangoClient.AddConnection("127.0.0.1", 8529, false, "Sample", "Sample");
var db = new ArangoDatabase("Sample");
string collectionName = "MyTestCollection";
var collection = new ArangoCollection();
collection.Name = collectionName;
collection.Type = ArangoCollectionType.Document;
if (db.Collection.Get(collectionName) == null)
{
db.Collection.Create(collection);
}
var employee = new Employee();
employee.Id = "1234";
employee.Name = "My Name";
employee.Salary = 33333;
employee.DateOfBirth = new DateTime(1979, 7, 22);
db.Document.Create<Employee>("MyTestCollection", employee);
employee.Name = "Tan";
db.Document.Update(employee);
It thrown the error for db.Document.Update(employee). Here's the error message: Field '_id' does not exist.
Then I tried to add the field _id though I think it is weird, it prompted me another error message.
Arango.Client.ArangoException : ArangoDB responded with error code BadRequest:
expecting PATCH /_api/document/<document-handle> [error number 400]
at Arango.Client.Protocol.DocumentOperation.Patch(Document document, Boolean waitForSync, String revision)
at Arango.Client.ArangoDocumentOperation.Update[T](T genericObject, Boolean waitForSync, String revision) ...
I have no clues at all and do not know how to to proceed further. Any help will be much appreciated. Thanks.
This is likely due to the definition of the Employee class, which is not contained in the above snippet.
To identify a document in a collection, documents have special system attributes, such as _id, _key and _rev. These attributes should be mapped to properties in .NET classes, even if not used explicitly. So one property in the class should be tagged with "Identity", one with "Key", and one with "Revision". Here is an example class definition that should work:
public class Employee
{
/* this will map the _id attribute from the database to ThisIsId property */
[ArangoProperty(Identity = true)]
public string ThisIsId { get; set; }
/* this will map the _key attribute from the database to the Id property */
[ArangoProperty(Key = true)]
public string Id { get; set; }
/* here is _rev */
[ArangoProperty(Revision = true)]
public string ThisIsRevision { get; set; }
public DateTime DateOfBirth { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
public Employee()
{
}
}
The ThisIsId property will contain the automatically assigned _id value, and can also be used to retrieve the document easily later:
var employeeFromDatabase = db.Document.Get<Employee>(employee.ThisIsId);
You can of course rename the properties to your like.
This code works fine.
using (ContextDB db = new ContextDB())
{
var custAcct = (from c in db.CustAccts
select new
{
c.AcctNo,
c.Company,
c.UserName
}).ToList();
But this one doesn't
public class CustAcct
{
public int AcctNo { get; set; }
public string Company { get; set; }
public string UserName { get; set; }
}
....
....
....
using (ContextDB db = new ContextDB())
{
CustAcct custAcct = (from c in db.CustAccts
select new
{
c.AcctNo,
c.Company,
c.UserName
}).ToList();
It returns this error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'EMailReader.Models.CustAcct'. An explicit conversion exists (are you missing a cast?)
I used Google, found many related topics but still couldn't put it to work using the available solutions
I just need to return data to a strong typed model.
EDITED:
After more research I found this solution bellow, but I wonder why I cannot retrieve directly in the list from LinqToSql.
List<CustAcct> temp = new List<CustAcct>();
IEnumerable<dynamic> items = custAcct;
foreach (var item in items)
{
temp.Add(new CustAcct()
{
AcctNo = item.AcctNo,
Company = item.Company,
UserName = item.UserName,
});
}
You are re defining those properties by creating new Class. And this will override LINQ2SQL generated class.
Just change "public class CustAcct" to "public partial class CustAcct".
This will solve your problem, and you do not need to define those properties again. Remove those from your class. Those will be automatically create for you.
If you can just post your class, and I will change it for you.
//Shyam