C# syntax challenges after resolving WCF errors post upgrade from 10.2.700

Hello all, I wonder if anyone has run into this data manipulation problem when upgrading. I’ve gone down every path I can think of and drawing a blank, and yet I think it’s just a syntax issue.

After using info here C# Customization Error After Upgrade

I’ve been able to upgrade most of our customizations. Our original integration partner used a template with a “GetNewBO” extension that stopped working due to WCF changes. Their template is beyond my skill level, and what little commenting there is isn’t in English, and appears to assume a much better programming knowledge than I have.

I’ve largely resolved it by replacing (for example)

using (var bo = oTrans.GetNewBO<SalesOrderImpl, SalesOrderSvcContract>())
{
     bool morePages = false;

     var ds = bo.GetRows("OrderNum = " + orderNum.ToString(), 
          "", "", "", "", "", "OrderLine = " + orderLine.ToString(), 
          "", "", "", "", "", "", "", "", "", "", -1, -1, out morePages);
}

with

using (var soBO = WCFServiceSupport.CreateImpl<SalesOrderImpl> 
                  ((Ice.Core.Session)oTrans.Session,SalesOrderImpl.UriPath))
{
	bool morePages = false;

	var soDS = soBO.GetRows("OrderNum = " + orderNum.ToString(), 
		"", "", "", "", "", 
		"OrderLine = " + orderLine.ToString(), 
		"", "", "", "", "", "", "", "", "", "", -1, -1, out morePages);
}

After enabling WCFServiceSupport using the script that ships with Kinetic, mentioned in the article referenced above.

Mostly I can get anything I want out of soDS. However, I’m running into cases where the original customization indexes objects and that’s no longer available. What little LINQ I’ve been able to use doesn’t seem to apply to whatever I’m outputting from GetRows() even though the two versions of BL Tester appear to do the same thing.

So for example, the original developer pulls a list of OrderRels like this in 10.2.700:

var availRels = ds.OrderRel.Where<SalesOrderDataSet.OrderRelRow>(r => ((Guid)r["VinID_c"]).ToString("D") == "00000000-0000-0000-0000-000000000000");

and now that throws

“Erp.BO.SalesOrderDataSet.OrderRelDataTable’ does not contain a definition for ‘Where’ and the best extension method overload ‘System.Linq.Queryable.Where(System.Linq.IQueryable, System.Linq.Expressions.Expression<System.Func<TSource,bool>>)’ has some invalid arguments”

I eventually abandoned that format of query and this worked:

var availRels = (from SalesOrderDataSet.OrderRelRow rmvRelRow in soDS.OrderRel
      where rmvRelRow["VinID_c"].ToString() == "00000000-0000-0000-0000-000000000000"     select rmvRelRow);

I was able to get data, for example var availQty = availRels.Sum(r => r.SellingReqQty); works fine.

I could also access individual column from the first row, availRels.AsEnumerable().FirstOrDefault()["WarehouseCode"].ToString() worked fine.

But some of the customizations use indexes in a loop, for example
orderRel.Plant = availRels[i].Plant;, which threw a new kind of error:

“Cannot apply indexing with [] to an expression of type ‘System.Collections.Generic.IEnumerable<Erp.BO.SalesOrderDataSet.OrderRelRow>’”

And where the original uses availRels.Count in a conditional, it also gave:

“Operator ‘>’ cannot be applied to operands of type ‘method group’ and ‘int’”

My output is still coming from GetRows() so I wonder if the implementation has changed in Kinetic?

At first I was able to solve this by adding .ToList(); to my query. Anywhere I’m only reading from the data, that list syntax and its associated indexing works the same as whatever was there before.

But now I’m running into loops where the var that is taken from soDS is being updated, and I’m not clear on how it gets back into soBO.Update(); if it has been modified with .ToList();

Anybody have any thoughts? In the meantime I’ll try to implement this with .ToList() and report what happens.

.

Hi Steve,

I think we were the original developers of that piece of code :slight_smile:

The first issue you can fix by editing the template code:

public static class SessionExt
{
    public static T GetNewBO<T, TContract>(this Ice.Core.Session session)
        where T : Epicor.ServiceModel.Channels.ImplBase
        where TContract : class
    {
        //return (T)Activator.CreateInstance(typeof(T), ((Ice.Core.Session)oTrans.Session).ConnectionPool);
        return WCFServiceSupport.CreateImpl<T>(session, Epicor.ServiceModel.Channels.ImplBase.GetUriPath(typeof(TContract)));
    }
}

The second,issue, why you cannot use Linq methods on a DataTable, is because it doesn’t implement IEnumerable. So instead, you can cast the DataRowCollection to an IEnumerable:

var availRels = ds.OrderRel.Rows.Cast<SalesOrderDataSet.OrderRelRow>().Where(r => (Guid)r["VinID_c"] == Guid.Empty);

The rest of your questions are basic Linq stuff… You cannot apply an indexer to an unsorted IEnumerable. Your workaround of using .ToList() here is correct. .ToList() only casts the collection to an ordered list, the objects it contains are still referencing the original rows. Everything in .NET is a reference type, apart from integral types and structures.

I was trying to not mention any names but yes, and thanks for jumping on it!

Thanks for the tip, it makes sense. Actually I’ve gone through a dozen customizations and removed the extensions. They seem to be very good tools, but a given customization only uses 1 or 2 of the tools available, and I’m trying to simplify. My thinking is to work each function inside the script towards standing by itself, in view of moving it to efx functions sooner or later so I can move to the Kinetic screens.

I’ll try the Rows.Cast, thanks for that, and many thanks for explaining the ToList() functionality. I was worried it would only read the data and not be able to update.

1 Like

So why did this

var availRels = ds.OrderRel.Where<SalesOrderDataSet.OrderRelRow>(r => .... etc

work before, but not now? Is there a change in Epicor? Or Microsoft? Or am I missing something else completely?

It’s working so it doesn’t matter, but I’m trying to understand better.

Yes. Microsoft did not originally include WCF in .NET Core (3+)/. There’s a lot of WCF out there, so Microsoft created a community-led project (CoreWCF) to provide most of WCF in .NET 6, which K2022 now uses, to support legacy solutions. Microsoft (and therefore Epicor) would prefer we stop using WCF at some point.

About CoreWCF

CoreWCF is a community-driven .NET Foundation project that makes WCF surface area available on modern versions of .NET. Although CoreWCF is not a Microsoft-owned project, Microsoft has announced that it will provide product support for CoreWCF. While newer technologies like gRPC and ASP.NET WebAPI are still recommended for new development, CoreWCF is a great option to help projects with existing heavy WCF dependencies move to .NET 6.

Although CoreWCF supports many common WCF scenarios, it does not support all WCF functionality. Your modernization experience may vary depending on the overlap between your WCF usage and the functionality included in CoreWCF. If functionality you depend on is not yet in CoreWCF, please provide feedback in the Feature Roadmap issue so that CoreWCF project maintainers can prioritize work based on community need.

1 Like

OK, now I get the connection between my problem and the WCF change.

But I still don’t get why the object being created used to be accessible with .Where(stuff == stuff) and now isn’t.

I’ll leave that for the far smarter programmers: Hugo, Jose, Olga, Josh, Haso, Conn, et. al.

:wink:

1 Like

Maybe you changed referenced assemblies and missed something like System.Data.DataSetExtensions.

1 Like

I don’t think so as I didn’t touch the existing ones, but thanks for the tip, I’ll check it out.

I’m guessing there was some extension method in the customization template. We don’t have one in there by default, but some developers add their own. Would need to see the original unmodified code to be sure.

2 Likes