New instance in RIA Domain Service - silverlight

I'm using .NET RIA Services July Preview to communicate between my Silverlight client and my server. On my server I have an ASP.NET project hosting the Silverlight application. In the ASP.NET application I have a Linq-to-Sql DataModel with a table called Hour. I have extended the Hour entity with 3 properties by creating Hour.shared.cs:
public partial class Hour
{
public string CustomerName { get; set; }
public string ProjectName { get; set; }
public string FullProjectName { get { return this.CustomerName + " - " + this.ProjectName; } }
}
In my domainservice I have a get-method called GetHours. Due the design in Linq, I cannot explicit create a new instance of the Hour entity and through the new entity set the values of my new properties:
var query = from hours in this.Context.Hours
select new Hour()
{
HourID = hours.HourID,
ProjectName = "Hello World"
};
If I just select hours it works just fine but I need to set the ProjectName and CustomerName some how.
Any ideas about how to get around this?

Trying my answer again, last time SO froze on me.
Probably the easiest way to do this would be using a function:
public Hour PopulateHour(Hour hour)
{
hour.CompanyName = "xyz";
hour.ProjectName = "123";
return hour;
}
Then you can use this in your linq query:
var query = from hour in this.Context.Hours
select PopulateHour(hour);
One thing that would make this easier would be if you already had the CompanyId and ProjectId as properties in the Hour class.

Related

Breeze->EF6 updating some changed columns, but not all

I've got a Breeze Context Provider talking to a EF 6.1.1, database-first, application with SQL Server that I'm having some trouble with. I can INSERT a new record, but when I update it, not all the changed columns get written to the database.
I have a generated POCO that looks like this:
public partial class Inventory
{
public Inventory()
{
}
public int Id { get; set; }
public System.DateTime EnteredAt { get; set; }
public string EnteredBy { get; set; }
public string UpdatedBy { get; set; }
public Nullable<System.DateTime> UpdatedAt { get; set; }
public string Comment { get; set; }
}
When I go and update an entity on client side (setting Comment property) and send it to Breeze, I do some very simple sets in a EFContextProvider::BeforeSaveEntity override:
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
// only one inventory is ever sent in
if (saveMap.ContainsKey(typeof(Inventory)))
{
var source = saveMap[typeof(Inventory)].First().Entity as Inventory;
// set up the user and time fields
if (source.Id <= 0)
{
source.EnteredBy = _defaultUserName;
source.EnteredAt = DateTime.Now;
}
else
{
source.UpdatedBy = _defaultUserName;
source.UpdatedAt = DateTime.Now;
}
}
}
But when the change gets committed, the changed UpdatedBy value never gets in to the database.
I turned on EF6 SQL logging and sure enough, the UPDATE statement completely misses the property.
UPDATE [dbo].[Inventory]
SET [Comment] = #0, [UpdatedAt] = #1
WHERE ([Id] = #2)
-- #0: '1532' (Type = AnsiString, Size = 250)
-- #1: '2/4/2016 10:32:58 PM' (Type = DateTime2)
-- #2: '100344' (Type = Int32)
-- Executing at 2/4/2016 3:33:06 PM -07:00
-- Completed in 7 ms with result: 1
Of course, UpdatedBy is NULL in the database for this update.
I can't figure out why this particular column will not do through when an 'adjacent' column, set at the same time, does. I also don't know if this is a Breeze problem or an EF problem since I can go back in and just using EF, DBContext all works fine.
I also tried deleting the table from the EDMX file and re-adding it to no avail. I re-verified that the column is in the table in the EDMX file.
As a hack, what I have to do is go back in, re-read the changed record directly, update it, then send it back.
Any advice would be appreciated.
Can you post the BeforeSaveEntity method and a sample manipulation of the entity on the client side into your question?
Secondly, make sure you're updating the propertyMap for the entity info in your BeforeSave method like so:
source.UpdatedBy = "Joe User";
source.UpdatedAt = DateTime.Now;
source.Comment = "1532";
entityInfo.OriginalValuesMap["UpdatedBy"] = null;
entityInfo.OriginalValuesMap["UpdatedAt"] = null;
entityInfo.OriginalValuesMap["Comment"] = null;
I'm wondering if the "UpdatedAt" property is manipulated on the client before calling saveChanges, and therefore this property would already by identified as 'Modified' in the property map.
please Refer this Question you will get answer.
how to secure add or delete entities with breezejs
Two option here :
1. change in actual saveMap entity not in new entity.
2. create new entity as 'source' and replace it with actual entity.

How to read, edit and export word documents in WPF without Microsoft office being installed?

I have an WPF application that relies heavily on manipulating documents; I want to know if there is a library that works independetly from Microsoft Office Word and that provides the following features:
Reading word documents (*.doc or rtf will be suffisiant, *.docx will be perfect)
Enable me to edit the document from my WPF app
Enable me to export again the document into other formats (word, excel, pdf)
Free :)
Thanks in advance.
I will try to answer in order:
Reading: This article is good for you.
Edit & export: May be this library works for you.
Free: The most difficult part of your question. You can do it for free using Interop Assemblies for Office. But controls for free... Many controls not free around the net.
Hope it helps.
I was faced with similar question some years ago. I had Windows forms application with some 20 reports and about 100 users and I needed to generate Word documents from application. Application was installed on a server. My first attempt was done by using Office interop, but it caused problems with performance and all kinds of unpredictable exceptions. So I started to look for alternatives and I soon landed with OpenXML.
First idea was that our team would use OpenXML SDK to generate and manipulate documents. It soon turned out that the learning curve was way too steep and our management wasn't willing to pay for the extra work.
So we started to look for alternatives. We didn't find any useful free library and so we tried some commercial ones (Aspose, Docentric). Aspose gave great results, but it was too expensive. Docentric's license is cheaper and the product performed well in Word document generation, so we finally decided to purchase it.
WHAT IT TAKES TO GENERATE A DOCUMENT FROM A TEMPLATE
Install Docentric Toolkit (you can get 30 day trial version for free)
In your VisualStudio project ad references to 4 Docentric dlls, which you can find in installation folder C:\Program Files (x86)\Docentric\Toolkit\Bin
Include Entity Framework via NuGet package If you will fill data from SQL database into the Word document
Prepare Word template, where you define layout and include fields which will get filled with data at document generation (see on-line documentation how to do it).
It doesn't take much code to prepare the data to be merged with the template. In my example I prepare order for customer "BONAP" from Northwind database. Orders include customer data, order details and product data. Data model also includes header and footer data.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Docentric.Word;
using System.Diagnostics;
namespace WordReporting
{
// Report data model
public class ReportData
{
public ReportData()
{ }
public string headerReportTemplatetName { get; set; }
public string footerDateCreated { get; set; }
public string footerUserName { get; set; }
public List<Order> reportDetails { get; set; }
}
// model extensions
public partial class Order
{
public decimal TotalAmount { get; set; }
}
public partial class Order_Detail
{
public decimal Amount { get; set; }
}
// Main
class Program
{
static void Main(string[] args)
{
// variable declaration
List<Order> orderList = new List<Order>();
string templateName = #"c:\temp\Orders_template1.docx";
string generatedDocument = #"c:\temp\Orders_result.docx";
// reading data from database
using (var ctx = new NorthwindEntities1())
{
orderList = ctx.Orders
.Include("Customer")
.Include("Order_Details")
.Include("Order_Details.Product")
.Where(q => q.CustomerID == "BONAP").ToList();
}
// collecting data for the report
ReportData repData = new ReportData();
repData.headerReportTemplatetName = templateName;
repData.footerUserName = "<user name comes here>";
repData.footerDateCreated = DateTime.Now.ToString();
repData.reportDetails = new List<Order>();
foreach (var o in orderList)
{
Order tempOrder = new Order();
tempOrder.Customer = new Customer();
tempOrder.OrderID = o.OrderID;
tempOrder.Customer.CompanyName = o.Customer.CompanyName;
tempOrder.Customer.Address = o.Customer.Address;
tempOrder.Customer.City = o.Customer.City;
tempOrder.Customer.Country = o.Customer.Country;
tempOrder.OrderDate = o.OrderDate;
tempOrder.ShippedDate = o.ShippedDate;
foreach (Order_Detail od in o.Order_Details)
{
Order_Detail tempOrderDetail = new Order_Detail();
tempOrderDetail.Product = new Product();
tempOrderDetail.OrderID = od.OrderID;
tempOrderDetail.ProductID = od.ProductID;
tempOrderDetail.Product.ProductName = od.Product.ProductName;
tempOrderDetail.UnitPrice = od.UnitPrice;
tempOrderDetail.Quantity = od.Quantity;
tempOrderDetail.Amount = od.UnitPrice * od.Quantity;
tempOrder.TotalAmount = tempOrder.TotalAmount + tempOrderDetail.Amount;
tempOrder.Order_Details.Add(tempOrderDetail);
}
repData.reportDetails.Add(tempOrder);
}
try
{
// Word document generation
DocumentGenerator dg = new DocumentGenerator(repData);
DocumentGenerationResult result = dg.GenerateDocument(templateName, generatedDocument);
// start MS Word and show generated document
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "WINWORD.EXE";
startInfo.Arguments = "\"" + generatedDocument + "\"";
Process.Start(startInfo);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
// wait for the input to terminate the application
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
}
}

DateCreated or Modified Column - Entity Framework or using triggers on SQL Server

After I read one question in attached link, I got a sense of how to set DateCreated and DateModified columns in Entity Framework and use it in my application. In the old SQL way though, the trigger way is more popular because is more secure from DBA point of view.
So any advice on which way is the best practice? should it be set in entity framework for the purpose of application integrity? or should use trigger as it make more sense from data security point of view? Or is there a way to compose trigger in entity framework? Thanks.
EF CodeFirst: Rails-style created and modified columns
BTW, even though it doesn't matter much, I am building this app using ASP.NET MVC C#.
Opinion: Triggers are like hidden behaviour, unless you go looking for them you usually won't realise they are there. I also like to keep the DB as 'dumb' as possible when using EF, since I'm using EF so my team wont need to maintain SQL code.
For my solution (mix of ASP.NET WebForms and MVC in C# with Business Logic in another project that also contains the DataContext):
I recently had a similar issue, and although for my situation it was more complex (DatabaseFirst, so required a custom TT file), the solution is mostly the same.
I created an interface:
public interface ITrackableEntity
{
DateTime CreatedDateTime { get; set; }
int CreatedUserID { get; set; }
DateTime ModifiedDateTime { get; set; }
int ModifiedUserID { get; set; }
}
Then I just implemented that interface on any entities I needed to (because my solution was DatabaseFirst, I updated the TT file to check if the table had those four columns, and if so added the interface to the output).
UPDATE: here's my changes to the TT file, where I updated the EntityClassOpening() method:
public string EntityClassOpening(EntityType entity)
{
var trackableEntityPropNames = new string[] { "CreatedUserID", "CreatedDateTime", "ModifiedUserID", "ModifiedDateTime" };
var propNames = entity.Properties.Select(p => p.Name);
var isTrackable = trackableEntityPropNames.All(s => propNames.Contains(s));
var inherits = new List<string>();
if (!String.IsNullOrEmpty(_typeMapper.GetTypeName(entity.BaseType)))
{
inherits.Add(_typeMapper.GetTypeName(entity.BaseType));
}
if (isTrackable)
{
inherits.Add("ITrackableEntity");
}
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", String.Join(", ", inherits)));
}
The only thing left was to add the following to my partial DataContext class:
public override int SaveChanges()
{
// fix trackable entities
var trackables = ChangeTracker.Entries<ITrackableEntity>();
if (trackables != null)
{
// added
foreach (var item in trackables.Where(t => t.State == EntityState.Added))
{
item.Entity.CreatedDateTime = System.DateTime.Now;
item.Entity.CreatedUserID = _userID;
item.Entity.ModifiedDateTime = System.DateTime.Now;
item.Entity.ModifiedUserID = _userID;
}
// modified
foreach (var item in trackables.Where(t => t.State == EntityState.Modified))
{
item.Entity.ModifiedDateTime = System.DateTime.Now;
item.Entity.ModifiedUserID = _userID;
}
}
return base.SaveChanges();
}
Note that I saved the current user ID in a private field on the DataContext class each time I created it.
As for DateCreated, I would just add a default constraint on that column set to SYSDATETIME() that takes effect when inserting a new row into the table.
For DateModified, personally, I would probably use triggers on those tables.
In my opinion, the trigger approach:
makes it easier; I don't have to worry about and remember every time I save an entity to set that DateModified
makes it "safer" in that it will also apply the DateModified if someone finds a way around my application to modify data in the database directly (using e.g. Access or Excel or something).
Entity Framework 6 has interceptors which can be used to set created and modified. I wrote an article how to do it: http://marisks.net/2016/02/27/entity-framework-soft-delete-and-automatic-created-modified-dates/
I agree with marc_s - much safer to have the trigger(s) in the database. In my company's databases, I require each field to have a Date_Modified, Date_Created field, and I even have a utility function to automatically create the necessary triggers.
When using with Entity Framework, I found I needed to use the [DatabaseGenerated] annotation with my POCO classes:
[Column(TypeName = "datetime2")]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? Date_Modified { get; set; }
[Column(TypeName = "datetime2")]
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime? Date_Created { get; set; }
I was attempting to use stored procedure mapping on an entity, and EF was creating #Date_Modified, #Date_Created parameters on my insert/update sprocs getting the error
Procedure or function has too many arguments specified.
Most of the examples show using [NotMapped], which will allow select/insert to work, but then those fields will not show up when that entity is loaded!
Alternately you can just make sure any sprocs contain the #Date_Modified, #Date_Created parameters, but this goes against the design of using triggers in the first place.

WCF RIA EntityQuery returns null for Foreign Keys

I'm following a tutorial on Silverlight and Prism, where WCF RIA Services are uses to access the Northwind database through an ADO.NET Entity Data Model.
In the Northwind database, there is a table Order_Details which is connected to two other tables (Orders and Products) via foreign keys:
The code to query the database looks like this:
EntityQuery<Order_Detail> detailQuery = _MyDomainContext.GetOrder_DetailsQuery();
_MyDomainContext.Load(detailQuery.Where(
det => det.Order.Customer.CustomerID == cust.CustomerID).OrderByDescending(
det => det.Order.OrderDate).Take(10), OnDetailsLoaded, null);
and then in OnDetailsLoaded:
var details = loadOp.Entities;
if (details != null)
{
var detailsList = details.ToList();
detailsList.ForEach(det => Orders.Add(
new OrderItem
{
ProductName = det.Product.ProductName,
Quantity = det.Quantity,
OrderDate = det.Order.OrderDate.Value
}));
This gives me a Null Exception at new OrderItem, because both Product and Order are null. I have set my EntityDataModel to "Include Foreign Key Columns in the Model". The code is exactly as in the tutorial, I only had to recreate the ADO.Net EntityDataModel and the DomainService to work with my local database, so the error is probably somewhere there.
How would I debug this error? I have no previous experience with databases. I'm using SQL Server 2012 with a Northwind database adapted for 2012, while the tutorial was written with SQL Server 2008 RC.
You probably need to include the Product / Order explicitly in the load method. Try doing a
.Include("Order").Include("Product")
inside the domain context load.
Here is what I had to do to solve the problem. Josh's answer was part of the solution, but I also had to add [Include] attributes to the metadata:
In MyDomainService.cs, add .Include():
public IQueryable<Order_Detail> GetOrder_Details()
{
return this.ObjectContext.Order_Details.Include("Order").Include("Product");
}
In MyDomainService.metadata.cs, add [Include]:
internal sealed class Order_DetailMetadata
{
// Metadata classes are not meant to be instantiated.
private Order_DetailMetadata()
{
}
[Include]
public Order Order { get; set; }
[Include]
public Product Product { get; set; }
}
}
This is described in detail at this website, thanks to Brian Noyes for pointing it out to me.

Passing computed values with RIA Services

I'm trying to figure out the a way to get extra data to be passed with the entities returned from a RIA domain service.
For example, let's say I want to display a DataGrid for "Orders" and include a column for the total items in an order.
Order Num. | Cust. Name | *No. of Items Ordered*
4545 | John | 4
1234 | Mike | 7
On the server side, with a Linq query, I could do:
var query =
from o in entities.Orders
select new OrderWithItemCount { Order = o, ItemCount = o.Items.Count() };
... and this will retrieve my orders along with the Items counts all in one go.
The problem is, I can't find anyway to propagate these results thru a domain service to the Silverlight client. I suppose I could use a standard WCF service, but what's the fun in that?
Update
What turned out to be the actual problem...
I had at one point actually already tried the "Easy way" that Nissan Fan and Florian Lim point out. When I tried it, I wasn't getting all my data. (I also need to include the customer Person in the query to get their name.) It turns out that what I thought was a limitation of RIA Services was actually a limitation of EF 4.0, in that saying entities.Orders.Include("Customer") won't work if you select a new type that isn't an Order. The work around is to explicitly select o.Customer in your select statement, and EF will automatically wire the selected Person into the assiocated property on Order.
Easy way:
Just add extra fields to your Order class (could be done in a partial class) and populate that in your DomainService.
Complicated but more flexible way:
Define OrderWithItemCount as an entity (needs to have a [Key] attribute), then transfer that.
public class OrderWithItemCount
{
[Key]
public int Id { get; set; }
// You need this, so that the DomainContext on the client can put them back together.
public int OrderId { get; set; }
[Include]
[Association("OrderReference", "OrderId", "Id")]
public Order Order { get; set; }
public int ItemCount { get; set; }
public Person Customer { get; set; }
}
public IQueryable<OrderWithItemCount> GetOrdersWithItemCount()
{
var query = from o in entities.Orders
select new OrderWithItemCount
{
OrderId = o.Id,
Order = o,
ItemCount = o.Items.Count,
Customer = o.Customer, // Makes sure EF also includes the Customer association.
};
return query;
}
There may be minor errors in the code, since I cannot test this at the moment, but I recently implemented something similar.
If you are using LINQ to SQL to produce your Domain Service you could simply go into the partial class for Orders and add a Property called NumberOfOrders which returns an Int representing the count. This property would carry through to the client without issue.
internal sealed class OrderMetadata
{
// Metadata classes are not meant to be instantiated.
private OrderMetadata()
{
}
... (property stubs auto-generated)
}
public int NumberOfOrders
{
get { return this.Items.Count; }
}
The reason why you cannot do this the way you demonstrated above is because you cannot marshal across anything but conrete classes (anonyous classes are a no-go). By adding this property to the partial class it will effectively be part of its published signature.

Resources